Qt显示实时图像,从大量信号中逃脱
Qt showing live image, escape from massive signals
我有一个 QThread 运行,试图解码来自相机的图像:
struct ImageQueue
{
enum {NumItems = 5};
tbb::concurrent_bounded_queue<DecodedImage> camera_queue_; // decoded image
tbb::concurrent_bounded_queue<DecodedImage> display_queue_; // widget display image
ImageQueue(int camid){camera_queue_.set_capacity(NumItems);display_queue_.set_capacity(NumItems)}
};
std::shared_ptr<ImageQueue> camera_queue;
void Worker::process()
{
while(1)
{
if(quit_)
break;
DecodedImage tmp_img;
camera_queue->camera_queue_.pop(tmp_img);
camera_queue->display_queue_.push(tmp_img);
emit imageReady();
}
emit finished();
}
并且此线程是相机的一部分 Class:
void Camera::Start()
{
work_ = new Worker();
work_->moveToThread(workerThread_);
QObject::connect(workerThread_, &QThread::finished, work_, &QObject::deleteLater);
QObject::connect(this, &Camera::operate, work_, &Worker::process);
QObject::connect(this, &Camera::stopDecode, work_, &Worker::stop);
QObject::connect(work_, &Worker::imageReady, this, &Camera::DisplayImage);
workerThread_->start();
emit operate();
}
在小部件显示方面:
class CVImageWidget : public QGraphicsView
{
Q_OBJECT
public:
void display(DecodedImage& tmp_img);
~CVImageWidget();
private:
QGraphicsScene *scene_;
};
CVImageWidget widget;
void Camera::DisplayImage()
{
if(camera_queue != nullptr)
{
DecodedImage tmp_img;
camera_queue->display_queue_.pop(tmp_img);
widget->display(tmp_img);
}
}
void CVImageWidget::display(DecodedImage& tmp_img)
{
if(!tmp_img.isNull())
{
scene_->clear();
scene_->addPixmap(QPixmap::fromImage(tmp_img));
}
}
我的问题是:
有没有办法让我免受大量 imageReady 信号的影响?我使用这个信号来显示图像,因为图像必须在主线程中显示,否则图像将不会显示。但是大量的信号会使GUI响应变慢。
有办法解决这个问题吗?谢谢。
互斥方法应该比发出 Qt 信号更快。我曾经尝试使用 STL condition_variable 跳过 Qt 信号,它对我来说效果很好。
您不希望每次图像到达时都改变图形场景。您也可以使用 QLabel
及其 setPixmap
方法,或简单的自定义小部件。
不需要显示队列:您只对最近的帧感兴趣。如果 UI 线程滞后于相机线程,您需要删除过时的帧。
您必须重新考虑 camera_queue
是否需要成为并发队列,因为您只能从单个线程访问它,以及您是否需要该队列。
典型的图像生产者-消费者的工作方式如下:Camera
class 与相机接口,并在每次获得新帧时发出 hasFrame(const DecodedImage &)
信号。不需要队列:任何监听对象线程的事件队列已经是并发队列了。
显示小部件仅接受要显示的图像:
class Display : public QWidget {
Q_OBJECT
DecodedImage m_image;
protected:
void paintEvent(QPaintEvent *event) {
QPainter painter{this};
painter.drawImage(0, 0, m_image);
}
public:
Display(QWidget *parent = nullptr) : QWidget{parent} {}
QSize sizeHint() const override { return m_image.size(); }
Q_SLOT void setImage(const QImage & image) {
auto oldSize = m_image.size();
m_image = image;
if (oldSize != m_image.size())
updateGeometry();
update();
}
};
然后将图像源连接到显示小部件,就完成了。 paintEvent
或其他 GUI 线程操作可以根据您的意愿进行,即使相机生成图像的速度比显示器消耗图像的速度快得多,您仍然会每次显示最新的图像小部件有机会重新绘制自己。有关在固定管道 OpenGL 后端上运行并使用 GPU 缩放图像的类似演示,请参阅 for a demonstration of that fact. See 。
我有一个 QThread 运行,试图解码来自相机的图像:
struct ImageQueue
{
enum {NumItems = 5};
tbb::concurrent_bounded_queue<DecodedImage> camera_queue_; // decoded image
tbb::concurrent_bounded_queue<DecodedImage> display_queue_; // widget display image
ImageQueue(int camid){camera_queue_.set_capacity(NumItems);display_queue_.set_capacity(NumItems)}
};
std::shared_ptr<ImageQueue> camera_queue;
void Worker::process()
{
while(1)
{
if(quit_)
break;
DecodedImage tmp_img;
camera_queue->camera_queue_.pop(tmp_img);
camera_queue->display_queue_.push(tmp_img);
emit imageReady();
}
emit finished();
}
并且此线程是相机的一部分 Class:
void Camera::Start()
{
work_ = new Worker();
work_->moveToThread(workerThread_);
QObject::connect(workerThread_, &QThread::finished, work_, &QObject::deleteLater);
QObject::connect(this, &Camera::operate, work_, &Worker::process);
QObject::connect(this, &Camera::stopDecode, work_, &Worker::stop);
QObject::connect(work_, &Worker::imageReady, this, &Camera::DisplayImage);
workerThread_->start();
emit operate();
}
在小部件显示方面:
class CVImageWidget : public QGraphicsView
{
Q_OBJECT
public:
void display(DecodedImage& tmp_img);
~CVImageWidget();
private:
QGraphicsScene *scene_;
};
CVImageWidget widget;
void Camera::DisplayImage()
{
if(camera_queue != nullptr)
{
DecodedImage tmp_img;
camera_queue->display_queue_.pop(tmp_img);
widget->display(tmp_img);
}
}
void CVImageWidget::display(DecodedImage& tmp_img)
{
if(!tmp_img.isNull())
{
scene_->clear();
scene_->addPixmap(QPixmap::fromImage(tmp_img));
}
}
我的问题是:
有没有办法让我免受大量 imageReady 信号的影响?我使用这个信号来显示图像,因为图像必须在主线程中显示,否则图像将不会显示。但是大量的信号会使GUI响应变慢。
有办法解决这个问题吗?谢谢。
互斥方法应该比发出 Qt 信号更快。我曾经尝试使用 STL condition_variable 跳过 Qt 信号,它对我来说效果很好。
您不希望每次图像到达时都改变图形场景。您也可以使用
QLabel
及其setPixmap
方法,或简单的自定义小部件。不需要显示队列:您只对最近的帧感兴趣。如果 UI 线程滞后于相机线程,您需要删除过时的帧。
您必须重新考虑
camera_queue
是否需要成为并发队列,因为您只能从单个线程访问它,以及您是否需要该队列。
典型的图像生产者-消费者的工作方式如下:Camera
class 与相机接口,并在每次获得新帧时发出 hasFrame(const DecodedImage &)
信号。不需要队列:任何监听对象线程的事件队列已经是并发队列了。
显示小部件仅接受要显示的图像:
class Display : public QWidget {
Q_OBJECT
DecodedImage m_image;
protected:
void paintEvent(QPaintEvent *event) {
QPainter painter{this};
painter.drawImage(0, 0, m_image);
}
public:
Display(QWidget *parent = nullptr) : QWidget{parent} {}
QSize sizeHint() const override { return m_image.size(); }
Q_SLOT void setImage(const QImage & image) {
auto oldSize = m_image.size();
m_image = image;
if (oldSize != m_image.size())
updateGeometry();
update();
}
};
然后将图像源连接到显示小部件,就完成了。 paintEvent
或其他 GUI 线程操作可以根据您的意愿进行,即使相机生成图像的速度比显示器消耗图像的速度快得多,您仍然会每次显示最新的图像小部件有机会重新绘制自己。有关在固定管道 OpenGL 后端上运行并使用 GPU 缩放图像的类似演示,请参阅