Я пытаюсь передать OpenCV Mat из потока захвата изображения в поток обработки изображения как часть большого приложения. У меня нет требования к потоку захвата изображения и потоку обработки изображения для одновременного доступа к мату. В результате я просто хочу передать право собственности на Mat из потока захвата изображения в поток обработки изображения. Я использую OpenCV 3.
Проблема, с которой я сталкиваюсь, заключается в том, что моя программа теряет большой объем памяти.
Ниже я приложил минимальный пример кода, который я использую для передачи циновок между потоками. Когда я запускаю этот код, он заканчивает использовать где-то между 10 МБ и 500 МБ памяти только из 300 изображений с разрешением 1640x1232.
Код
#include <iostream>
#include <thread>
#include <mutex>
#include <opencv2/opencv.hpp>
#include <signal.h>
#include <stdio.h>
#define SIM_PROCESSING_TIME_MS 150
using namespace std;
using namespace cv;
static volatile int keepRunning = 1;
static volatile int runThread = 1;
// Acts as a stack where the last image to be added is the first to be processed.
// If the 'capture' rate is higher than the processing rate then some images are skipped and processed at the end once the 'capture' has stopped.
vector<Mat> unprocessedImages;
mutex unprocessedImageMutex;
void intHandler(int dummy)
{
keepRunning = 0;
}
// Simulates a function which captures an image using opencv.
Mat GetImage()
{
return imread("../0000.jpg", CV_LOAD_IMAGE_COLOR);
}
// Simulates an image processing thread.
// A delay has been used to replace any actual image processing as in my testing it didn't seem to make a difference.
void image_processing_thread()
{
int count = 0;
while(true)
{
Mat imageToProcess;
{// lock the vector and remove the last element
lock_guard<mutex> lk(unprocessedImageMutex);
if (unprocessedImages.size() > 0)
{
imageToProcess = unprocessedImages.back();
unprocessedImages.pop_back();
}
}
if(!imageToProcess.empty())
{
// We have an image to process so sleep to simulate processing
this_thread::sleep_for(std::chrono::milliseconds(SIM_PROCESSING_TIME_MS));
count++;
cout << "Processed " << count << endl;
}
else if(!runThread) //The image loading thread is done and there are no more images to process
break;
this_thread::sleep_for(chrono::milliseconds(1));
}
}
// Simulates the image capture thread.
// 'Captures' images then pushes them onto unprocessedImages which the image processing thread then reads from.
int main()
{
signal(SIGINT, intHandler);
// Start thread to process images
auto imageProcessingThread = std::thread(image_processing_thread);
// Load 300 images into memory
for (int count = 0; count < 300; count++)
{
this_thread::sleep_for(std::chrono::milliseconds(20));
auto img = GetImage();
lock_guard<mutex> lk(unprocessedImageMutex);
unprocessedImages.push_back(img);
}
// Allow processing thread to exit when it has finished
runThread = 0;
cout << "All images loaded in" << endl;
imageProcessingThread.join();
cout << "All images processed" << endl;
while (keepRunning) {}
return 1;
}
Есть немного кода, чтобы выйти из программы с помощью SIGINT. Этот код не является частью моего более крупного приложения
Попытки исправить
- Вызов
unprocessedImages.reserve(1000)
наверху основного.
- Замена
unprocessedImages
на std::array
и индекс.
- Замена
unprocessedImages
массивом c и индексом.
- Использование
copyTo
для перемещения матов из unprocessedImages
.
- Все вышеперечисленное при переносе
Mat
в shared_ptr
или unique_ptr
(например, vector<unique_ptr<Mat>> unprocessedImages;
).
Ни одна из этих попыток не повлияла на характеристики утечки памяти.
Вопрос
Что вызывает утечку памяти в моей программе? Как вы собираетесь передавать права собственности на OpenCV Mats между разными потоками?
Спасибо, Джеймс
Редактировать: Добавлено дополнительное исправление.
Редактировать 2: Работа с Valgrind приводит к тому, что приведенный выше код НЕ протекает. Это подтверждается отчетом Valgrind, в котором говорится, что все блоки были доступны после завершения программы. Из распечаток программ ясно, что запуск его в valgrind сделал его однопоточным приложением, так как операторы print внутри двух потоков идеально чередуются.
Редактировать 3: я изменил main
, как показано ниже. В результате максимальное и минимальное использование памяти на каждой итерации внешнего цикла было одинаковым. На этом конкретном прогоне минимальное использование памяти было 374 МБ.
int main() {
signal(SIGINT, intHandler);
while(keepRunning)
{
runThread = 1;
// Start thread to process images
auto imageProcessingThread = std::thread(image_processing_thread);
/* ... */
cout << "All images processed" << endl;
}
while (keepRunning) {}
return 1;
}