我正在运行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();
}
此线程是Camera 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响应变慢。
有没有办法解决这个问题?谢谢。
答案 0 :(得分:0)
互斥方法应该比发出Qt信号更快。我曾经尝试使用STL condition_variable跳过Qt信号,它对我来说效果很好。
答案 1 :(得分:0)
每次图像到达时,您都不希望改变图形场景。您也可以使用QLabel
及其setPixmap
方法或简单的自定义小部件。
不需要显示队列:您只对最近的帧感兴趣。如果UI线程滞后于相机线程,则需要删除过时的帧。
如果camera_queue
需要是并发队列,您必须重新考虑,因为您只从一个线程访问它,以及您是否需要该队列。
典型的图像生成者 - 消费者将按如下方式工作:Camera
类与相机接口,并在每次获得新帧时发出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线程操作可以按照您的意愿进行,即使相机生成的图像比显示器消耗它们的速度快得多,每次小部件获取时,您仍然会显示最新的图像重塑自己的机会。请参阅this answer以了解该事实。请参阅this answer,了解适用于固定管道OpenGL后端的类似演示,并使用GPU缩放图像。