I've implemented a processing pipeline using a producer-consumer pattern. A consumer waits for data to process as a independent thread and then handles the data as soon as it receives it, while a producer will push data into the processing queue of a consumer. Well, that's the simplified version...
As long as I use cv::Mat all is fine. But, when using cv::UMat to enable GPU support, the program is behaving strange (having timing issues). It's like sometimes data has not finished processed or copied back from the GPU to CPU, yet...
Code to reproduce issue
Here is some simplified example:
(1) a producer is loading an image and forwards copies of it to a consumer.
(2) the consumer just displays the received images in a window.
Using cv::Mat works... cv::UMat produces an empty window most of the time; sometimes some random data appears and sometimes the delivered image is shown. When add an intermediate consumer, that e.g. applys a grayscale conversion followed by a canny edge detection, the shown image is sometimes the grayscale image and sometimes the canny edge detection.
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
template <typename T>
class thread_queue {
private:
std::queue<T> queue;
std::mutex mutex;
std::condition_variable newdata;
public:
thread_queue<T>() {}
~thread_queue<T>() {}
void push(T t) {
std::unique_lock<std::mutex> lock(mutex);
queue.push(t);
newdata.notify_one();
}
T pop() {
std::unique_lock<std::mutex> lock(mutex);
if (queue.empty()) newdata.wait(lock);
T elem = queue.front();
queue.pop();
return elem;
}
size_t getSize() {
std::unique_lock<std::mutex> lock(mutex);
return queue.size();
}
};
/* use either UMat or Mat ... */
// #define myMAT UMat
#define myMAT Mat
thread_queue<myMAT*> queue;
bool shutdown = false;
void Producer() {
// Load Image...
myMAT image;
cv::imread("/PATH/TO/ANY/IMAGE.JPG", CV_LOAD_IMAGE_COLOR).copyTo(image);
while (!shutdown) {
// Push copy to output queue...
cv::myMAT* copy = new myMAT();
image.copyTo(*copy);
queue.push(copy);
// Wait some time...
std::chrono::milliseconds time(100); // 100ms => 10 fps
std::this_thread::sleep_for(time);
}
}
void Consumer() {
while (!shutdown) {
myMAT *image = queue.pop();
if (image == NULL) break;
// Display image
imshow("OUTPUT", *image);
// call waitKey (else window content isn't updated)
waitKey(1);
}
}
void main()
{
// Start workers...
std::thread *producer= new std::thread(Producer);
std::thread *consumer = new std::thread(Consumer);
getchar(); // Wait for key pressed....
// Shutdown...
shutdown = true;
producer->join();
queue.push(NULL); // shutdown signal...
consumer->join();
}
Questions
(1) What is possibly causing that issue?
(2) How to fix it?