0

I have a QThread running, trying to decode image from a camera:

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();
}

And this thread is part of the 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();
}

On the widget display side:

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));
    }
}

My question is:

Is there a way to save me from the massive imageReady signals ? I use this signal to display image because the image has to be shown in main thread otherwise the image will not be displayed. But the massive amount of signals will make the GUI response become slower.

Is there a way to fix that ? Thanks.

Johnnylin
  • 507
  • 2
  • 7
  • 26

2 Answers2

0

Mutex approach should be faster than emiting Qt signals. I've once tried to skip Qt signals by using STL condition_variable and it worked just fine for me.

Seph
  • 1
0
  1. You do not want to be altering a graphics scene each time an image arrives. You can just as well use a QLabel and its setPixmap method, or a simple custom widget.

  2. There is no need for a display queue: you're only interested in the most recent frame. If the UI thread lags behind the camera thread, you want to drop obsolete frames.

  3. You have to rethink if the camera_queue needs to be a concurrent queue, since you access it from a single thread only, and whether you need that queue at all.

The typical image producer-consumer would work as follows: a Camera class interfaces with a camera and emits a hasFrame(const DecodedImage &) signal each time it got a new frame. No need for a queue: any listening object thread's event queues are concurrent queues already.

A display widget simply accepts the images to display:

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();
  }
};

You then connect the image source to the display widget and you're done. The paintEvent or other GUI thread operations can take as long as you wish, even if the camera produces images much faster than the display can consume them, you'll still show the most recent image each time the widget gets a chance to repaint itself. See this answer for a demonstration of that fact. See this answer for a similar demonstration that works on a fixed-pipeline OpenGL backend and uses the GPU to scale the image.

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Thanks for the detailed answer. Can you share a complete demo code of this logic ? – Johnnylin Nov 22 '16 at 18:45
  • I've linked two complete examples. If you're looking for something else, let me know. – Kuba hasn't forgotten Monica Nov 22 '16 at 19:39
  • The DecodedImage structure is a self defined struct that contains a QImage as well as other information. Can I use the signal to send it and also enjoy implicit sharing? – Johnnylin Nov 23 '16 at 01:13
  • Yes, as long as the other information is reasonably cheap to copy. Or you can wrap the other information in a `QSharedData` or hold it using a `QSharedPointer`. – Kuba hasn't forgotten Monica Nov 23 '16 at 01:24
  • Also, this logic requires to send massive signals to display widget, which might slow down gui response. My program also use signals, but when displaying image, if the user clicks some buttons in the gui very often, the gui response tend to get slower and slower because of the massive signals sent by producer. – Johnnylin Nov 23 '16 at 01:35
  • Sorry, but I have no idea what you mean by "massive signals". Frequent? Signals with large data that's expensive to copy? The "user clicking some buttons in the gui very often" is not a sensible description of what's going on. Provide a minimal test case. Replace long running operations with `QThread::msleep`. It should be very obvious what you're doing wrong, then. If not, ask a question with a self-contained test case! You should have profiler traces that show what code is taking the time. Your assertion about "massive signals" being the culprit better had hard data behind it. – Kuba hasn't forgotten Monica Nov 23 '16 at 01:53
  • TL;DR: All I know is that sending big data using signal/slot mechanism isn't a problem if you're doing it right. And I can't tell what you're doing wrong without seeing the code that actually is the source of the problem. What I've shown above works: there are two **complete** examples ready for you to clone, compile and play with (and many others in the same repository). They work. What **specific question** do you still have unanswered? Edit your question to state that. At the moment, your question is answered and that's that. – Kuba hasn't forgotten Monica Nov 23 '16 at 01:56
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/128766/discussion-between-johnnylin-and-kuba-ober). – Johnnylin Nov 23 '16 at 02:58