相机流式传输时 Qt GUI 线程没有响应

Qt GUI thread is not responding when camera is streaming

我正在使用 Basler 相机流式传输图像并同时分析图像。 该程序在子项目静态库中有摄像头class、图像检测class、图像采集class。在另一个 GUI 子项目中显示流的 GUI。但是,当程序为 运行 时,GUI 在摄像头流式传输时保持“无响应”。我哪里做错了,这种场景需要实现QThread吗?如果我必须使用 QThread,我应该在主 GUI 线程还是在 Acquisition 中使用它? 相机 class 从 Basler 相机抓取图像流:

class ICam: public QObject
    ,public Pylon::CImageEventHandler  {
    Q_OBJECT
    public:
        ICam(QObject *parent = nullptr);
        Mat GetOpencvImage();
        void StartContinuousGrabbing();
    signals:
        void OneImageFinishSignal();
    protected:
        virtual void OnImageGrabbed(Pylon::CInstantCamera& camera, const Pylon::CGrabResultPtr& grabResult);
    private:
        CBaslerUniversalInstantCamera m_camera;
};

void ICam::OnImageGrabbed(Pylon::CInstantCamera& camera, const Pylon::CGrabResultPtr& grabResult)
{
    clock.Lock();
    m_ptr_grab_result = grabResult;//Pass the captured image out
    m_bitmap_image.CopyImage(grabResult);
    clock.Unlock();
    emit OneImageFinishSignal();
}

Mat ICam::GetOpencvImage(){
    return cv::Mat(m_bitmap_image.GetHeight(), m_bitmap_image.GetWidth(), CV_8UC3, (uint8_t *)m_bitmap_image.GetBuffer());
}

void ICam::StartContinuousGrabbing(){
    m_camera.StartGrabbing( Pylon::GrabStrategy_OneByOne, Pylon::GrabLoop_ProvidedByInstantCamera);
}

检测class做图像处理检测虹膜眼:

class Detect : public QObject
{
    Q_OBJECT
    public:
       explicit Detect(QObject *parent = nullptr);
       cv::Point CalculateIrisOffset(cv::Mat img_input);
 };


cv::Point Detect::CalculateIrisOffset(cv::Mat img_input, bool show) {

    //Some Code to detect the iris

    return center_offset;
}

Acquisition class包含一个icam对象class和一个detect对象class作为成员属性,它在抓取图像时接收来自icam对象的信号并向GUI发送信号显示图像,同时调用Detectclass的detect函数对图像进行处理:

class Acquisition: public QObject
{
    Q_OBJECT
    public:
        Acquisition (QObject *parent = nullptr);
        void StartContinuousGrabbing();
        Mat GetOpenCVImageFromICam();
    signals:
        void OneImageFinishSignal();
    private slots:
        void OneImageFinishSlot();
    private:
        ICam *icam;
        Detect *detect;
};
Acquisition:: Acquisition(QObject *parent) : QObject(parent)
                                                    ,icam(new ICam())
                                                    ,detect(new Detect())
{
    //connect(this->icam, SIGNAL(OneImageFinishSignal()), this, SIGNAL(OneImageFinishSignal()));
    connect(this->icam, SIGNAL(OneImageFinishSignal()), this, SLOT(OneImageFinishSlot()));
}

void Acquisition::OneImageFinishSlot(){
    cv::Mat img_input= icam-> GetOpencvImage ();
    cv::Point center_iris_offset;
    center_offset = detect->CalculateOffset(img_input, 0);
    emit(OneImageFinishSignal());
}
void Acquisition::StartContinuousGrabbing(){
    this->icam->StartContinuousGrabbing();
}
Mat CDLImageAcquisition::GetOpenCVImageFromICam(){
    return this->icam_->GetOpencvImage();
}

主 GUI class:

class MainWizard : public QWizard
{
    Q_OBJECT
    public:
        explicit MainWizard(QWidget *parent = nullptr);
    private slots:
        void OneImageFinishSlot();
        void ShowImage(QWidget *object, Mat image);
    private:
        virtual bool eventFilter(QObject *watched, QEvent *event);
        Acquisition *acquisition;
};

MainWizard::MainWizard(QWidget *parent) :
    QWizard(parent),
    ui(new Ui::MainWizard), 
    acquisition(new Acquisition())
{
    ui->setupUi(this);
    ui->dock_cnt_continuous_grab->installEventFilter(this);//Install Qt's event filter
    acquisition ->StartContinuousGrabbing();
    connect(acquisition, SIGNAL(OneImageFinishSignal()), this, SLOT(OneImageFinishSlot()));
}

void MainWizard::OneImageFinishSlot(){
    ui->dock_cnt_continuous_grab->update();

}

bool MainWizard::eventFilter(QObject *watched, QEvent *event)
{
  if (watched == ui->dock_cnt_continuous_grab && event->type() == QEvent::Paint)
  {
      cv::Mat opencv_image = acquisition->GetOpenCVImageFromICam();
      this->ShowImage(ui->dock_cnt_continuous_grab, opencv_image);
  }
  return false;
}

void MainWizard::ShowImage(QWidget *widget, Mat image)
{
    m_mutex_lock.lock();
    QPainter painter(widget);

    QImage img((const unsigned char *)(image.data), image.cols, image.rows, QImage::Format_RGB888);

    QRectF target;
    target.setLeft(0);
    target.setTop(0);
    target.setSize(this->size());

    QRectF source;
    source.setLeft(0);
    source.setTop(0);
    source.setSize(img.size());

    QPixmap pixmap = QPixmap::fromImage(img);
    painter.drawImage(target,img, source);
    painter.drawPixmap(target, pixmap, source);
    m_mutex_lock.unlock();
}

我终于通过一些更改解决了这个问题。我认为主要问题是抓取的帧太大,程序无法同时在 GUI 上分析和显示。初始抓取的图像大小为4000*3096*3。在通过信号发送之前,我将高度和宽度调整了 8 倍,并为图像处理的每个维度再次调整了 2 倍。此外,我发出携带新抓取图像的信号,而不是像以前那样发出空信号。在 GUI 方面,我使用 QGraphicsView 和 QGraphicsItems 而不是绘画事件过滤器。这是详细代码:

class ICam: public QObject
,public Pylon::CImageEventHandler  {
Q_OBJECT
public:
    ICam(QObject *parent = nullptr);
    void StartContinuousGrabbing();
signals:
    void OneImageFinishSignal(Mat img);
protected:
    virtual void OnImageGrabbed(Pylon::CInstantCamera& camera, const Pylon::CGrabResultPtr& grabResult);
private:
    CBaslerUniversalInstantCamera m_camera;
    CPylonImage pylon_image;
    CImageFormatConverter format_converter;// in constructor: format_converter.OutputPixelFormat = PixelType_BGR8packed;
};

void ICam::OnImageGrabbed(Pylon::CInstantCamera& camera, const Pylon::CGrabResultPtr& grabResult)
{
    clock.Lock();
    m_ptr_grab_result = grabResult;//Pass the captured image out
    m_bitmap_image.CopyImage(grabResult);
    format_converter.Convert(pylon_image, grabResult);
    clock.Unlock();

    cv::Mat img_resize;
    resize(opencv_image_, img_resize, cv::Size(opencv_image_.cols/8,
    opencv_image_.rows/8), 0, 0, cv::INTER_AREA);
    //Tell the gui that there is a new image available
    emit OneImageFinishSignal(img_resize.clone());
 }


void ICam::StartContinuousGrabbing(){
    m_camera.StartGrabbing( Pylon::GrabStrategy_OneByOne, Pylon::GrabLoop_ProvidedByInstantCamera);
}

收购class:

class Acquisition: public QObject
{
    Q_OBJECT
    public:
        Acquisition (QObject *parent = nullptr);
    signals:
        void OneImageFinishSignal(Mat img);
    private slots:
        void OneImageFinishSlot(Mat img_input);
    private:
        ICam *icam;
        Detect *detect;
};
Acquisition:: Acquisition(QObject *parent) : QObject(parent)
                                                ,icam(new ICam())
                                                ,detect(new Detect(2))//resize by 2
{

    connect(this->icam, SIGNAL(OneImageFinishSignal()), this, SLOT(OneImageFinishSlot()));
}

void Acquisition::OneImageFinishSlot(Mat img_input){
    cv::Point center_iris_offset;
    center_offset = detect->CalculateOffset(img_input, 0);
    emit OneImageFinishSignal(img_input);
}

主 GUI class:

class MainWizard : public QWizard
{
    Q_OBJECT
    public:
        explicit MainWizard(QWidget *parent = nullptr);
        QImage PutImage(const Mat& mat);
    private slots:
        void OneImageFinishSlot(Mat img);
    private:
        QGraphicsScene* scene;
        QGraphicsPixmapItem* pixmap_item;
        QMutex m_mutex_lock;
        cv::Mat opencv_image;            
        Acquisition *acquisition;
};

MainWizard::MainWizard(QWidget *parent) :
    QWizard(parent),
    ui(new Ui::MainWizard), 
    acquisition(new Acquisition())
{
    ui->setupUi(this);
    scene = new QGraphicsScene(this);
    pixmap_item = new QGraphicsPixmapItem();
    scene->addItem(pixmap_item);

    acquisition ->StartContinuousGrabbing();
    connect(acquisition, SIGNAL(OneImageFinishSignal(Mat)), this, SLOT(OneImageFinishSlot(Mat)));
}

void MainWizard::OneImageFinishSlot(Mat img){
    m_mutex_lock.lock();
    pixmap_item->setPixmap(QPixmap::fromImage(PutImage(img)));
    scene->update();

    ui->gphv_continuous_grab->setScene(scene);
    ui->gphv_continuous_grab->update();

    m_mutex_lock.unlock();
}


/*Convert openCV image to QImage*/
QImage MainWizard::PutImage(const Mat& mat)
{
    // 8-bits unsigned, NO. OF CHANNELS=1
    if(mat.type()==CV_8UC1){
        // Set the color table (used to translate colour indexes to qRgb values)
        QVector<QRgb> colorTable;
        for (int i=0; i<256; i++)
            colorTable.push_back(qRgb(i,i,i));
        // Copy input Mat
        const uchar *qImageBuffer = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage img(qImageBuffer, mat.cols, mat.rows, QImage::Format_Indexed8);
        img.setColorTable(colorTable);
        return img;
    }
    // 8-bits unsigned, NO. OF CHANNELS=3
    if(mat.type()==CV_8UC3){
        // Copy input Mat
        const uchar *qImageBuffer = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage img(qImageBuffer, mat.cols, mat.rows, QImage::Format_RGB888);
        return img.rgbSwapped();
    }else{
        qDebug() << "ERROR: Mat could not be converted to QImage.";
        return QImage();
    }
}