OpenCV: processing multiple images in a C++ vector using pthreads - c++

I have a large number images in a file that I need to perform various processing operations on. Here is what I am trying to do
1) Read the images into a file, and put them in a C++ vector named imageQueue (a mutable array)
2) Create a number of threads
3) Each thread grabs an image from imageQueue, and then erases that image from the vector
4) Each thread then goes ahead and processes that image
5) When finished processing, each thread grabs the next image from the vector
6) This entire process runs until there are no more images in the imageQueue, at which point the program ends. (currently I have 4 photos in the file that I am using for tests, which is why in my loops I run from i = 0 to i < 4. When I complete this, I will have many more photos.
I have named each of the images in the file 00.jpg, 01.jpg, 02.jpg....
For testing purposes, right now I am simply trying to have each thread display the image it grabbed. However, when I run this only purely white windows pop up, instead of the actual image. Any help on why this is happening and how to correct it?
Thanks!
Here is my code:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <pthread.h>
#include <vector.h>
#define NUM_THREADS 2
using namespace std;
using namespace cv;
/* Function Declarations */
void* startProcessing(void* args);
/* Global Variables */
vector <Mat> imageQueue;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int main(int argc, char** argv)
{
VideoCapture cap;
cap.open("/Users/nlauer/Documents/ImageSequence/%02d.jpg");
for(int i = 0; i < 4; i++)
{
Mat frame;
vector<Mat>::iterator it;
cap >> frame;
it = imageQueue.end();
it = imageQueue.insert(it, frame);
}
/* Create the threads */
pthread_t tids[NUM_THREADS];
for(int i = 0; i < NUM_THREADS; i++)
{
pthread_create(&tids[i], NULL, startProcessing, NULL);
}
/* Reap the threads */
for(int i = 0; i < NUM_THREADS; i++)
{
pthread_join(tids[i], NULL);
}
imageQueue.clear();
return 0;
}
void* startProcessing(void* args)
{
/* Each thread grabs an image from imageQueue, removes it from the
queue, and then processes it. The grabbing and removing are done
under a lock */
Mat image;
Mat emptyImage;
/* Get the first image for each thread */
pthread_mutex_lock(&mutex);
if(!imageQueue.empty()) {
image = imageQueue[0];
vector<Mat>::iterator it;
it = imageQueue.begin();
it = imageQueue.erase(it);
}
pthread_mutex_unlock(&mutex);
while(!image.empty())
{
/* Process the image - right now I just want to display it */
namedWindow("window", CV_WINDOW_AUTOSIZE);
imshow("window", image);
sleep(10);
/* Obtain the next image in the queue */
pthread_mutex_lock(&mutex);
if(!imageQueue.empty()) {
image = imageQueue[0];
vector<Mat>::iterator it;
it = imageQueue.begin();
it = imageQueue.erase(it);
} else {
image = emptyImage;
}
pthread_mutex_unlock(&mutex);
}
return NULL;
}

What you try to achieve in the items 3)-5) is exactly what Intel's library TBB is designed for. Take a look at tbb::parallel_for.
All you need is a class which has operator(Mat) which processes a single image, the library TBB will take care of the thread handling.
You can go even further if you use tbb::concurrent_queue instead of your vector to keep the images. Then processing can start even while reading has not finished. You might use tbb::parallel_do instead of tbb::parallel_for in this approach.

Issue has been resolved - I simply went ahead and continued with the processing as is. Something strange was happening when I tried to show the image in each thread, but as long as you let each thread finish its routine, then show the image after, everything worked fine.

Related

Why do I get corrupted results if I run GPU dense optical flow algorithm concurrently?

While using OpenCV CUDA dense optical flow in parallel I noticed that sometimes I get corrupted optical flow results, though I run it on different cuda::GpuMats and in separate cuda::Streams with separate Algorithm instances.
After some experiments with the code I found out that if I protect DenseOpticalFlow::calc() call with mutex or run only one thread, I always get correct (not corrupted) results.
I reduced my code to minimum reproducible example where I run optical flow algorithm on same input images multiple times in multiple threads:
#include <thread>
#include <vector>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat frames[2] = { imread("im0.jpg"), imread("im1.jpg") };
for (int i = 0; i < 2; i++) cvtColor(frames[i], frames[i], COLOR_BGR2GRAY);
Size frameSize = frames[0].size();
vector<thread> workers;
mutex m;
for (int id = 0; id < 2; ++id) // 2 threads
workers.emplace_back([&, id]()
{ // lambda
// Separate Stream, Algorithm and GpuMats for each thread
auto algoGpu = cuda::OpticalFlowDual_TVL1::create();
cuda::Stream stream;
cuda::GpuMat optFlow(frameSize, CV_32FC2);
cuda::GpuMat gpuFrame[2] = {
cuda::GpuMat(frameSize, CV_8UC1),
cuda::GpuMat(frameSize, CV_8UC1)
};
Mat downloaded, converted;
Mat channels[2] = { Mat(), Mat() };
for (int i = 0; i < 2; ++i) gpuFrame[i].upload(frames[i], stream);
for (int i = 0; i < 1000; i++) { // run 1000 times with SAME input
{
//unique_lock<mutex> l(m); // WORKS OK IF UNCOMMENTED !!!!!
algoGpu->calc(gpuFrame[0], gpuFrame[1], optFlow, stream);
}
if (id == 0) { // show results from same single thread
optFlow.download(downloaded, stream);
stream.waitForCompletion();
split(downloaded, channels);
channels[0].convertTo(converted, CV_8UC1, 100, 128);
imshow("flow", converted);
waitKey(1);
}
}
stream.waitForCompletion();
});
for (auto& worker : workers) worker.join();
return 0;
}
If I uncomment //unique_lock<mutex> l(m); I get same valid result image for every call. But if I leave it commented, approximately half of the results are corrupted.
Input images:
Correct result:
Examples of corrupted result:
I use OpenCV 4.0 with CUDA 10.1.
This issue is now resolved by this PR, and will be reflected in next release of OpenCV.

How to solve image processing camera IO delay with OpenCV

I am having an OpenCV program which works like this:
VideoCapture cap(0);
Mat frame;
while(true) {
cap >> frame;
myprocess(frame);
}
The problem is if myprocess takes a long time which longer than camera's IO interval, the captured frame be delayed, cannot get the frame synchronized with the real time.
So, I think to solve this problem, should make the camera streaming and myprocess run parallelly. One thread does IO operation, another does CPU computing. When the camera finished capture, send to work thread to processing.
Is this idea right? Any better strategy to solve this problem?
Demo:
int main(int argc, char *argv[])
{
cv::Mat buffer;
cv::VideoCapture cap;
std::mutex mutex;
cap.open(0);
std::thread product([](cv::Mat& buffer, cv::VideoCapture cap, std::mutex& mutex){
while (true) { // keep product the new image
cv::Mat tmp;
cap >> tmp;
mutex.lock();
buffer = tmp.clone(); // copy the value
mutex.unlock();
}
}, std::ref(buffer), cap, std::ref(mutex));
product.detach();
while (cv::waitKey(20)) { // process in the main thread
mutex.lock();
cv::Mat tmp = buffer.clone(); // copy the value
mutex.unlock();
if(!tmp.data)
std::cout<<"null"<<std::endl;
else {
std::cout<<"not null"<<std::endl;
cv::imshow("test", tmp);
}
}
return 0;
}
Or use a thread keep clearing the buffer.
int main(int argc, char *argv[])
{
cv::Mat buffer;
cv::VideoCapture cap;
std::mutex mutex;
cap.open(0);
std::thread product([](cv::Mat& buffer, cv::VideoCapture cap, std::mutex& mutex){
while (true) { // keep product the new image
cap.grab();
}
}, std::ref(buffer), cap, std::ref(mutex));
product.detach();
int i;
while (true) { // process in the main thread
cv::Mat tmp;
cap.retrieve(tmp);
if(!tmp.data)
std::cout<<"null"<<i++<<std::endl;
else {
cv::imshow("test", tmp);
}
if(cv::waitKey(30) >= 0) break;
}
return 0;
}
The second demo I thought shall be work base on https://docs.opencv.org/3.0-beta/modules/videoio/doc/reading_and_writing_video.html#videocapture-grab, but it's not...
In project with Multitarget tracking I used 2 buffers for frame (cv::Mat frames[2]) and 2 threads:
One thread for capturing the next frame and detect objects.
Second thread for tracking the detected objects and draw result on frame.
I used index = [0,1] for the buffers swap and this index was protected with mutex. For signalling about the end of work was used 2 condition variables.
First works CatureAndDetect with frames[capture_ind] buffer and Tracking works with previous frames[1-capture_ind] buffer. Next step - switch the buffers: capture_ind = 1 - capture_ind.
Do you can this project here: Multitarget-tracker.

Is there a way to avoid buffering images when using RTSP camera in OpenCV?

Let's say we have a time consuming process that when we call it on any frame, it takes about 2 seconds for it to complete the operation.
As we capture our frames with Videocapture, frames are stored in a buffer behind the scene(maybe it's occurred in the camera itself), and when process on the nth frame completes, it grabs next frame ((n+1)th frame), However, I would like to grab the frames in real-time, not in order(i.e. skip the intermediate frames)
for example you can test below example
cv::Mat frame;
cv::VideoCapture cap("rtsp url");
while (true) {
cap.read(frame);
cv::imshow("s",frame);
cv::waitKey(2000); //artificial delay
}
Run the above example, and you will see that the frame that shows belongs to the past, not the present. I am wondering how to skip those frames?
NEW METHOD
After you start videoCapture, you can use the following function in OpenCV.
videoCapture cap(0);
cap.set(CV_CAP_PROP_BUFFERSIZE, 1); //now the opencv buffer just one frame.
USING THREAD
The task was completed using multi-threaded programming. May I ask what's the point of this question? If you are using a weak processor like raspberrypi or maybe you have a large algorithm that takes a long time to run, you may experience this problem, leading to a large lag in your frames.
This problem was solved in Qt by using the QtConcurrent class that has the ability to run a thread easily and with little coding. Basically, we run our main thread as a processing unit and run a thread to continuously capture frames, so when the main thread finishes processing on one specific frame, it asks for another frame from the second thread. In the second thread, it is very important to capture frames without delay. Therefore, if a processing unit takes two seconds and a frame is captured in 0.2 seconds, we will lose middle frames.
The project is attached as follows
1.main.cpp
#include <opencv2/opencv.hpp>
#include <new_thread.h> //this is a custom class that required for grab frames(we dedicate a new thread for this class)
#include <QtConcurrent/QtConcurrent>
using namespace cv;
#include <QThread> //need for generate artificial delay, which in real situation it will produce by weak processor or etc.
int main()
{
Mat frame;
new_thread th; //create an object from new_thread class
QtConcurrent::run(&th,&new_thread::get_frame); //start a thread with QtConcurrent
QThread::msleep(2000); //need some time to start(open) camera
while(true)
{
frame=th.return_frame();
imshow("frame",frame);
waitKey(20);
QThread::msleep(2000); //artifical delay, or a big process
}
}
2.new_thread.h
#include <opencv2/opencv.hpp>
using namespace cv;
class new_thread
{
public:
new_thread(); //constructor
void get_frame(void); //grab frame
Mat return_frame(); //return frame to main.cpp
private:
VideoCapture cap;
Mat frame; //frmae i
};
3.new_thread.cpp
#include <new_thread.h>
#include <QThread>
new_thread::new_thread()
{
cap.open(0); //open camera when class is constructing
}
void new_thread::get_frame() //get frame method
{
while(1) // while(1) because we want to grab frames continuously
{
cap.read(frame);
}
}
Mat new_thread::return_frame()
{
return frame; //whenever main.cpp need updated frame it can request last frame by usign this method
}

capture frames after every 2 seconds

I am doing a project on face detection from video. I detected faces from the video, but it is capturing every frame, so i am getting so many images with in a second itself (so many frames got captured in a second).
Problem: I want to reduce that, capture frame after every 3 second is enough. I tried to use wait() ,sleep() functions. But they are just stop running the video for sometime,nothing else is happening. Can any one help me to overcome from this.
#include <cv.h>
#include <highgui.h>
#include <time.h>
#include <stdio.h>
using namespace std;
IplImage *frame;
int frames;
void facedetect(IplImage* image);
void saveImage(IplImage *img,char *ex);
IplImage* resizeImage(const IplImage *origImg, int newWidth,int newHeight, bool keepAspectRatio);
const char* cascade_name="haarcascade_frontalface_default.xml";//specify classifier cascade.
int k;
int main(int argc, char** argv)
{
// OpenCV Capture object to grab frames
//CvCapture *capture = cvCaptureFromCAM(0);
CvCapture *capture=cvCaptureFromFile("video.flv");
//int frames=cvSetCaptureProperty(capture,CV_CAP_PROP_FPS, 0.5);
//double res1=cvGetCaptureProperty(capture,CV_CAP_PROP_POS_FRAMES);
//cout<<"res"<<res<<endl;
// start and end times
time_t start, end;
// fps calculated using number of frames / seconds
double fps;
// frame counter
int counter = 0;
// start the clock
time(&start);
//while(cvGrabFrame(capture))
while(1)
{
//if(capture.get(CV_CAP_PROP_POS_FRAMES) % 2 == 0)
frames=cvSetCaptureProperty(capture,CV_CAP_PROP_FPS, 0.5);
if(frames%2==0)
{
frame = cvQueryFrame(capture);
cout<<"Frame"<<frame<<endl;
facedetect(frame);
}
}
cvReleaseCapture(&capture);
return 0;
}
I gave cvWaitKey(2000) after every frame is captured.
This would have been my trial. It saves one image per 30 frames. when you say too many images in one second, I understand that you are referring to saved faces.
int counter = 0;
// start the clock
time(&start);
//while(cvGrabFrame(capture))
while(1)
{
frame = cvQueryFrame(capture);
cout<<"Frame"<<frame<<endl;
if(count%30==0)
{
facedetect(frame);
}
count++;
}
if you really meant of skipping the frames, then try this. one frame per second might be the outcome of below code.
while(1)
{
if(count%30==0)
{
frame = cvQueryFrame(capture);
cout<<"Frame"<<frame<<endl;
facedetect(frame);
}
count++;
}
You can try to call waitKey(2000) after each capturing.
Note that the function will not wait exactly 2000ms, it will wait at least 2000ms, depending on what else is running on your computer at that time.
To achieve accurate frame rate, you can set the frame rate of capturing by:
cap.set(CV_CAP_PROP_FPS, 0.5);
Me personally, i would recommend using a modulo operator on the current frame like %2 == would check for every second frame.
if(capture.get(CV_CAP_PROP_POS_FRAMES) % 2 == 0)
//your code to save
Changing 2 to 3 or 5 you can define the offset.

OpenCV & Virsual C++: save Frame data from Camera and then save to Jpg

I am an OpenCV and C++ beginner. I've got a problem with my student project.My Tutor wants to grab frames from a Camera and save the grabbed frames into jpg. So first I used "cvCreateCameraCapture,cvQueryFrame,cvSaveImage" and it worded ok. But the frame is relative big,about 2500x2000,and it takes about 1 second to save one Frame. But my Tutor requires at least to save 10 Frames per second.
Then I came out the ideas to save raw data first and after grabbing process I can save them into Jpg. So I wrote following test code.But the problem is that all the saved Images are the same and it seems they are just from the data of the last grabbed frame.I guess the problem is about my poor knowledge of c++ especially pointers.So I really hope to get help here.
Thanks in advance!
void COpenCVDuoArryTestDlg::OnBnClickedButton1()
{
IplImage* m_Frame=NULL;
TRACE("m_Frame initialed");
CvCapture * m_Video=NULL;
m_Video=cvCreateCameraCapture(0);
IplImage**Temp_Frame= (IplImage**)new IplImage*[100];
for(int j=0;j<100;j++){
Temp_Frame[j]= new IplImage [112];
}
TRACE("Tempt_Frame initialed\n");
cvNamedWindow("video",1);
int t=0;
while(m_Frame=cvQueryFrame(m_Video)){
for(int k=0;k<m_Frame->nSize;k++){
Temp_Frame[t][k]= m_Frame[k];
}
cvWaitKey(30);
t++;
if(t==100){
break;
}
}
for(int i=0;i<30;i++){
CString ImagesName;
ImagesName.Format(_T("Image%.3d.jpg"),i);
if(cvWaitKey(20)==27) {
break;
}
else{
cvSaveImage(ImagesName, Temp_Frame[i]);
}
}
cvReleaseCapture(&m_Video);
cvDestroyWindow("video");
TRACE("cvDestroy works\n");
delete []Temp_Frame;
}
If you use C++, why don't you use the C++ opencv interface?
The reason you get N times the same image is that the capture reuses the memory for each frame, if you want to store the frames you need to copy them. Example for the C++ interface:
#include <vector>
#include "cv.h"
#include "highgui.h"
using namespace cv;
int main(int, char**)
{
VideoCapture cap(0); // open the default camera
if(!cap.isOpened()) // check if we succeeded
return -1;
Mat edges;
namedWindow("image",1);
std::vector<cv::Mat> images(100);
for(int i = 0; i < 100;++i) {
// this is optional, preallocation so there's no allocation
// during capture
images[i].create(480, 640, CV_8UC3);
}
for(int i = 0; i < 100;++i)
{
Mat frame;
cap >> frame; // get a new frame from camera
frame.copyTo(images[i]);
}
cap.release();
for(int i = 0; i < 100;++i)
{
imshow("image", images[i]);
if(waitKey(30) >= 0) break;
}
// the camera will be deinitialized automatically in VideoCapture destructor
return 0;
}
Do you have a multicore/multiple CPU system? Then you could farm out the 1second tasks across 16cores and save 16frames/second !
Or you could write your own optimized jpeg routine on the GPU in Cuda/OpenCL.
If you need it to run for longer you could dump the raw image data to disk, then read it back in later and convert to jpeg. 5Mpixel * 3color * 10fps is 150Mb/s (thanks etarion!) which you can do with two disks and windows Raid.
edit: If you only need to do 10frames then just buffer them in memory and then write them out as the other answer shows.
Since you already know how to retrieve a frame, check this answer:
openCV: How to split a video into image sequence?
This question is a little different because it retrieves frames from an AVI file instead of a webcam, but the way to save a frame to the disk is the same!