大家好我用Qt制作了一个程序,可以并行使用网络摄像头(每个网络摄像头分配到一个不同的线程,我检索流)。我的目标是能够以固定的时间间隔从我的所有网络摄像头拍摄照片。例如,每秒所有网络摄像头都会拍照并将其保存到计算机中。
但是我在使用QThreads时遇到了问题。该程序完成我所描述的,但我的线程永远不会死,所以我的程序在执行结束时不会关闭。为了理解和构建我的Qthread代码,我使用了这个article和one(对不起,它是法语,但它与第一个大致相同)。
我让Gui问用户:
我的程序使用2个类:
一旦用户输入了所有信息,他就会点击连接到SLOT的名为launch的OK按钮。 这是我简化的代码:
Sequence.h
class Sequence : public QWidget
{
Q_OBJECT
public:
Sequence(QWidget *parent = 0);
void InitialisationGUI();
~Sequence();
private:
QGroupBox* path; // This is for layout organisation puspose
QLineEdit* main_folder; // Holds the Global path for all the Folders where the photos will be
QLineEdit* fruit; //Folder where you want to save this sequence of photos
QLineEdit* cam; // holds the number of webcams the user entered
QLineEdit* intervalle; // holds the timelapse between each take
QLineEdit* nombre; // holds the number of photo per webcam
QVector<QThread*> threads; // vector of threads
QVector<Webcam*> webcams; // vector of webcam
public slots:
void ExplorerOpener(); // Opens a window for the user to easily choose his folder
void PhotoTakerLauncher(); // Creates the objects, threads and launch it all
};
Sequence.cpp
#include "sequence.h"
Sequence::Sequence(QWidget *parent)
: QWidget(parent)
{
threads = QVector<QThread*>();
webcams = QVector<Webcam*>();
InitialisationGUI();
}
Sequence::~Sequence()
{
}
void Sequence::InitialisationGUI()
{
// Here is the Initialisation of all the GUI Elements, but taking too much lines to put in here
// Launch button OK
QPushButton *launch = new QPushButton("OK");
// Organisation of the widget layout
QGridLayout *formulaire = new QGridLayout;
formulaire->addWidget(launch,5,2,Qt::AlignRight);
setLayout(formulaire);
//Connection Signaux Slots
connect(launch,SIGNAL(clicked()),this,SLOT(PhotoTakerLauncher()));
}
void Sequence::PhotoTakerLauncher()
{
// For each webcam i create an object webcam and a thread
for(int i=0; i<cam->text().toInt();i++)
{
// As the number of webcam is an unknow variable i came up with this way of doing but i am afraid it may also be the cause of my problem
// I create a new thread and append it into my vector
threads.append(new QThread);
// does the same for my webcam object
webcams.append(new Webcam(0,main_folder->text() + "/" + fruit->text(), i,intervalle->text(),nombre->text()));
// i move my object into the thread for the operation not to be in my UI Thread
webcams[i]->moveToThread(threads[i]);
// Connection For Initialisation and Termination of the Thread
connect(threads[i],SIGNAL(started()),webcams[i],SLOT(Streamer()));
// THOSE ARE THE CONNECTIONS THAT DOESN'T REACT WHEN MY PROGRAM IS FINISHED
connect(webcams[i],SIGNAL(finishedSequence()),threads[i],SLOT(quit()));
connect(webcams[i],SIGNAL(finishedSequence()),webcams[i],SLOT(deleteLater()));
connect(threads[i],SIGNAL(finished()),threads[i],SLOT(deleteLater()));
threads[i]->start();
}
// This is not important i used it for debug it is what assured me that none of my threads were quitting
for(int i=0;i<threads.count();i++)
{
/* threads[i]->wait();
cout << "Thread de caméra numéro: " + QString::number(i + 1).toStdString() << endl;
delete webcams[i];*/
}
}
webcam.h
class Webcam : public QObject
{
Q_OBJECT
public:
explicit Webcam(QObject *parent = 0);
Webcam(QObject *parent = 0,QString path= NULL, int number = 1, QString time = "1000", QString count = "100");
bool getStreaming();
void StopStreaming();
void test();
signals:
void finishedSequence(); // the signal I emit once all my photos are taken
public slots:
void ImagesRetriever(); // slot, called by a timer, which saves the image streaming
void Streamer(); // Retrieve the stream of the webcam and initializes the timer
private:
// CvCapture* capture; // old way
VideoCapture capture; // Holds the stream of the webcam
Mat frame;
string window_name = "Capture";
bool streaming;
QDir folder; // path to save the image
int iterator; // to number the image
QTimer* timer;
int intervalle; // the timelapse between each take
int nombre_photo; // number of photo
int nombre_cam; // it is the number of this camera
QString folder_path;
};
webcam.cpp
Webcam::Webcam(QObject *parent, QString path, int number, QString time, QString count)
: QObject(parent)
{
// Initialization of variables needed
folder_path = path;
nombre_cam = number;
intervalle = time.toInt();
nombre_photo = count.toInt();
streaming = false;
iterator = 1;
timer = new QTimer(this);
std::cout << "Initialisation Complete"<< std::endl;
}
// Slot launched once the thread has started
void Webcam::Streamer()
{
capture.open(nombre_cam);
if(capture.isOpened())
{
streaming = true;
// Connects on timeout
connect(timer,SIGNAL(timeout()),this,SLOT(ImagesRetriever()));
timer->start(intervalle);
}
else
{
// Emit the finished signal which should kill the thread but doesn't
emit finishedSequence();
}
}
void Webcam::ImagesRetriever()
{
// I stop and then relaunch the timer every time because the image saving can take up some time and for short timers it can create some conflict
timer->stop();
// Check that the stream is sill opened
if(capture.isOpened())
{
capture >> frame;
// check that an image has been retrieved
if(!frame.empty())
{
// Saving the image
QString image_path = folder_path + "/imagecam" + QString::number(nombre_cam + 1) + "_" + QString::number(iterator) + ".jpg";
imwrite(image_path.toStdString(),frame);
iterator ++;
}
else
{
cout << "Image non Récupérée -- Break"<<endl;
}
}
// while we still have photos to take
if(iterator <= nombre_photo)
{
timer->start(intervalle);
}
else
{
// we emit the signal which cause me PROBLEMS
emit finishedSequence();
}
}
我试图解释这一切,但如果你认为我做得不好,请不要犹豫。
我对为什么我的线程没有被杀死感到有点无能为力。目前我的想法是,由于QVector,Qt无法保存我的信号参数的副本以将其传递到插槽。但由于我对此不太熟悉,我无法说出来。
我的另一个想法是,可能视频流没有被杀死,因此对象和线程无法被杀死,但看到API我的印象不是。
有什么想法吗?
编辑1:回答我的线索问题
我终于找到了如何解决我的问题,我有很好的方法,但我的信号已完成序列连接到太多的插槽,需要按顺序完成,而不是按随机顺序。
首先,我向网络摄像头课程添加了一个新信号:
signals:
void finishedSequence();
void destroyedWebcam();
并修改默认析构函数以在代码中添加我的新信号:
Webcam::~Webcam()
{
cout << " We are in the destructor" << endl;
emit destroyedWebcam();
}
然后我将sequence.cpp中的连接更改为:
connect(threads[i],SIGNAL(started()),webcams[i],SLOT(Streamer()));
connect(webcams[i],SIGNAL(destroyedWebcam()),threads[i],SLOT(quit()));
connect(webcams[i],SIGNAL(finishedSequence()),webcams[i],SLOT(deleteLater()));
connect(threads[i],SIGNAL(finished()),threads[i],SLOT(deleteLater()));
然后我在我的Sequence类中添加了一个Slot来清除所有线程和网络摄像头对象,一旦它们全部完成,以便能够再次启动它:
void Sequence::Reinitialization()
{
cout << "Thread Destroyed => Reinitialization of threads and webcams" << endl;
count ++;
if(count == cam->text().toInt())
{
threads.clear();
webcams.clear();
cout << "QVectors cleared" << endl;
count =0;
}
}
最后我添加了一个新连接:
connect(threads[i], SIGNAL(destroyed()),this,SLOT(Reinitialization()));
这对我来说非常适合2个网络摄像头。现在我可以重复该程序,而不必每次都杀死它。因此,如果您想使用这种方式进行线程,那么它可以正常工作。
然而,我正面临一个新问题。当我连接3个网络摄像头时,我只能检索2,当3个网络摄像头中的一个是我的集成网络时会执行。我将搜索为什么以及如何解决此问题。
编辑2:对相机问题数量的回答
这是一个很大的问号,为什么我不能在我的电脑上使用3个网络摄像头知道我有两个USB控制器,而且usb速度足以在同一总线上支持两个。经过研究,我发现我的一个控制器专用于我的集成网络摄像头,而不是我的任何USB端口使用。所以最后我只有一个控制器和3个网络摄像头的总线。我所做的就是检索所有3个是降低图像的分辨率(到720p)并将帧速率降低到7fps,之后它运行良好。
我的项目终于完成了。希望我的反思,研究和答案能够帮助他人。