QImage没有通过信号槽Qt

时间:2018-03-27 09:14:08

标签: c++ qt

我正在构建一个应用程序,它在一个线程中使用OpenCV从IP摄像头获取图像,然后在处理后将Mat转换为QImage,然后将QImage发送到信号,然后图像将由相应的插槽在GUI线程中接收。

信号在处理映像线程类中声明:

void sendImage(const QImage &frame);

在GUI线程类中声明了插槽:

void onGetImage(const QImage &img);

但之后,当我在QLabel上显示图像或将图像保存到磁盘时,程序崩溃了。

我对C++有点新,所以对QImage对象的传递引用有什么问题吗?如果是,那么在没有任何复制(重新分配内存)开销的情况下在线程之间传递对象的正确方法是什么?因为我想在长时间运行的应用程序中防止甚至很小的开销。

更新:有时会运行一段时间,然后崩溃,有时甚至会在第一帧崩溃。

谢谢!

代码:处理图片

class ImageProcess
{
public:
    ImageProcess(std::function<void(cv::Mat&)> imgCallback):imgCallback(imgCallback){}
public:
    void start()
    {
        while(start) // loop till start become false
        {
            videpCapture.read(frame);
            // .... some process on image
            imgCallback(frame)
        }
    }
private:
    cv::Mat frame;
    std::function<void(cv::Mat&)> imgCallback;
};

代码:在图像处理单元和GUI之间进行通信

class WorkerThread: public QObject
{
    Q_OBJECT
public:
    WorkerThread()
    {
         imgProcess = new ImageProcess(std::bind(.....)); // bind the callback
    }

public slots:
    void onStart()
    {
        if(imProcessThread != nullptr)
        {
            imgProcess.start = false; // stop if running
            imProcessThread->quit(); // quit the thread
            imProcessThread->wait(); // wait to finish
            delete imProcessThread; // delete the pointer
        }
        imProcessThread = new ImageProcessThread(imgProcess); // create
        imProcessThread->start(); // start thread
    }
signals:
    void sendMessage(const QString& msg, const int& code);
private:
    ImageProcess *imgProcess;
    void frameCallback(const cv::Mat& frame); // frame callback
    {
        emit sendImage(matToQImage(frame)); // send the image to UI
    }
    // Mat to QImage converter
    QImage matToQImage(const cv::Mat &mat)
    {
        cv::Mat rgbMat;
        if(mat.channels() == 1) { // if grayscale image
            return QImage((uchar*)mat.data, mat.cols, mat.rows, (int)mat.step, 
    QImage::Format_Indexed8);// declare and return a QImage
        } else if(mat.channels() == 3) { // if 3 channel color image
            cv::cvtColor(mat, rgbMat, CV_BGR2RGB); // invert BGR to RGB
            return QImage((uchar*)rgbMat.data, mat.cols, mat.rows, 
    (int)mat.step, QImage::Format_RGB888);// declare and return a QImage
        }
        return QImage();
    }
    // image process thread
    class ImageProcessThread : public QThread
    {
    public:
        ImageProcessThread(ImageProcess *ip) : ip(ip){}
    protected:
        void run()
        {
            ip->start();
        }
    private:
        ImageProcess *ip;
    } *imProcessThread = nullptr;
};

代码:用户界面

class Camera : public QWidget
{
    Q_OBJECT
public:
    explicit Camera(QWidget *parent = 0)
    {
        wt = new WorkerThread;
        thread = new QThread;
        //conncet the signal-slots
        connect(wt, &WorkerThread::sendImage, this, &Camera::onGetImage);
        connect(ui->btStart, &QPushButton::clicked, wt, &WorkerThread::start);
    }
private slots:
    void onGetImage(const QImage &img)
    {
        // set the image to QLabel
    }
private:
    WorkerThread *wt;
    QThread *thread;
};
wt.moveToThread(thread);
thread->start();

那么为此目的可以设计一个goog?

2 个答案:

答案 0 :(得分:3)

由于您没有提供mcve我猜,但您对matToQImage的实施看起来有点怀疑。你有......

QImage matToQImage (const cv::Mat &mat)
{
  cv::Mat rgbMat;
  if (mat.channels() == 1) {
    return QImage((uchar*)mat.data, mat.cols, mat.rows, (int)mat.step, QImage::Format_Indexed8);
  } else if (mat.channels() == 3) {
    cv::cvtColor(mat, rgbMat, CV_BGR2RGB);
    return QImage((uchar*)rgbMat.data, mat.cols, mat.rows, (int)mat.step, QImage::Format_RGB888);
  }
  return QImage();
}

但是,从documentation开始,QImage构造函数用于转换cv::Mat ...

  

使用给定的宽度,高度和格式构造图像   现有的内存缓冲区,数据。宽度和高度必须是   以像素为单位指定bytesPerLine指定每个字节数   line(stride)。

     

缓冲区必须在QImage的整个生命周期内保持有效   未经修改或以其他方式分离的副本   原始缓冲区 [我的重点]。图像不会在销毁时删除缓冲区。   你可以提供一个函数指针cleanupFunction和一个额外的   指针cleanupInfo将在最后一个副本时被调用   破坏。

所以返回的QImage可能在调用cv::Mat析构函数后引用了悬空指针。

如果这是问题,那么最简单的解决方案是制作QImage的{​​{3}}并返回...

QImage matToQImage (const cv::Mat &mat)
{
  cv::Mat rgbMat;
  if (mat.channels() == 1) {
    return QImage((uchar*)mat.data, mat.cols, mat.rows, (int)mat.step, QImage::Format_Indexed8).copy();
  } else if (mat.channels() == 3) {
    cv::cvtColor(mat, rgbMat, CV_BGR2RGB);
    return QImage((uchar*)rgbMat.data, mat.cols, mat.rows, (int)mat.step, QImage::Format_RGB888).copy();
  }
  return QImage();
}

答案 1 :(得分:0)

接受 @ G.M。的回答,我想补充一些我在这个问题中发现的细节,以帮助其他人提出同样的问题。

问题在于将cv::Mat转换为QImage时,图片数据未复制到QImage,因此cv:MatQImage共享相同的数据。因为有两个线程同时运行,那么当创建QImage然后它发送到UI线程时,此时,处理映像线程(后台工作线程)已经清理了以前的数据在UI线程完成刷新图像作业之前的下一帧。所以该计划已经崩溃。