【问题标题】:open webcamera with OpenCV and show it with QLabel - white window使用 OpenCV 打开网络摄像头并使用 QLabel 显示它 - 白色窗口
【发布时间】:2013-01-21 16:07:15
【问题描述】:

我使用 OpenCV 和 Qt 库以及 VS 2010 在 Win7 x64 上工作。

我想用 OpenCV 打开我的相机,然后在从 Mat 转换为 QImage 之后用 Qt 显示捕获的帧,例如使用 QLabel。

我想这样做是因为使用函数 imshow("camera", image) 和 waitKey() 会减慢流式摄像头的速度。

这是我的代码:

int main () {
 QApplication a(argc, argv);
 QLabel myLabel;
 VideoCapture cap(0);
 //namedWindow(c"camera", 1);

 for (;;) {

    cap >> image;
        //conversion from Mat to QImage
    Mat dest;
    cvtColor(image, dest,CV_BGR2RGB);
    QImage image1= QImage((uchar*) dest.data, dest.cols, dest.rows, dest.step, QImage::Format_RGB888);

        //show Qimage using QLabel
    myLabel.setPixmap(QPixmap::fromImage(image1));
    myLabel.show();
    //imshow("camera",image);
    //if (waitKey(30)>= 0)  break;
 }
return a.exec();
}   

网络摄像头已正确打开并且可以正常工作,但我看到的是一个白色窗口,而不是捕获的帧,正如您在这张图片中看到的那样

如果我取消注释:namedWindow (..), imshow(..), if(waitKey(..),它可以工作(我看到两个窗口具有相同的图像),但我使用 OpenCV 显示捕获的帧,这是我想要避免的。

我的问题是:我做错了什么??我不知道,从 Mat 到 Qimage 的转换是错误的??? 或者,我不能仅使用 Qt 显示捕获的帧吗?

谢谢!

【问题讨论】:

    标签: c++ qt opencv video-capture qlabel


    【解决方案1】:

    我没有太多经验,但我可以看到这里可能出了什么问题:

     for (;;) {
    
        cap >> image;
            //conversion from Mat to QImage
        Mat dest;
        cvtColor(image, dest,CV_BGR2RGB);
        QImage image1= QImage((uchar*) dest.data, dest.cols, dest.rows, dest.step, QImage::Format_RGB888);
    
            //show Qimage using QLabel
        myLabel.setPixmap(QPixmap::fromImage(image1));
        myLabel.show();
        //imshow("camera",image);
        //if (waitKey(30)>= 0)  break;
     }
    

    您在死循环中执行此操作 - 这将导致您的 QLabel 无限更新自身,因此您可能看不到任何内容。此外,如果取消注释 waitKey 对您有帮助,那几乎意味着您正在将数据转换为 QImage,但是其他的东西被破坏了。

    请注意,a.exec() 永远不会执行,因为您将陷入循环,但我想这足以满足这个概念。

    为了不卡住事件循环,您需要创建QTimer 并每隔 x 毫秒更新您的小部件:

     class VideoWindow: public QWidget
     {
        Q_OBJECT
        public:
            VideoWindow(QWidget* parent = 0): QWidget(parent), cap(0)
            {
                timer = new QTimer(this);
                connect(timer, SIGNAL(timeout()), this, SLOT(updatePicture()));
                timer->start(20);
            }
    
    
        public slots:
            void updatePicture()
            {
                cap >> image;
                //conversion from Mat to QImage
                Mat dest;
                cvtColor(image, dest,CV_BGR2RGB);
                QImage image1 = QImage((uchar*) dest.data, dest.cols, dest.rows, dest.step, QImage::Format_RGB888);
    
                //show Qimage using QLabel
                setPixmap(QPixmap::fromImage(image1));
            }
    
        private:
            QTimer * timer;
            VideoCapture cap;
    };
    
    int main(int argc, char** argv)
    {
        QApplication app(argc, argv);
        VideoWindow w;
        w.show();
    
        app.exec();
        return 0;   
    }
    

    【讨论】:

    • 在主线程中休眠会和for循环一样的效果。您不能阻塞主线程并返回到事件循环。使用 QTimer 或单独的线程。
    • 非常感谢您的回答!我试图将您的代码复制到我的文件 .cpp(称为:“main_proiezione.cpp”)中,但是当我执行它时,我在终端上收到此消息:Object::connect: No such slot QWidget::updatePicture() in c:\ ... (我的“main_proiezione.cpp”文件的路径)。抱歉,我对编程不太擅长。
    • 我的答案中忘记了 Q_OBJECT 宏!立即尝试。
    • @Cristina1986 另外,如果您在没有 Qt 插件的情况下手动编译它,请不要忘记对其运行 moc.exe,并将 VideoWindow 类移动到单独的头文件中。
    • 这段代码有错别字吗?构造函数中开始 connect 的行末尾缺少一个括号。
    【解决方案2】:

    在您要用于显示图像的小部件(小部件/主窗口)的paintEvent() 中尝试此操作。

    在小部件的构造函数中打开相机:

    VideoCapture myVideo;
    myVideo.open(0);
    if(!myVideo.isOpened())
       cout<<"CANNOT OPEN CAMERA"<<endl; //or you can put some error message
    

    paintEvent() 中这样做

    myVideo >> frame;
    QImage image = QImage((const unsigned char*)frame.data,frame.cols,
                   frame.rows,frame.step,QImage::Format_RGB888);
    
    QRectF target(0.0,0.0,image.width(),image.height());
    QRectF source(0.0,0.0,image.width(),image.height());
    QPainter painter(this);
    painter.drawImage(target,image,source)
    

    您可以使用计时器将timeout SIGNAL 连接到小部件的update SLOT。使用 20-40 毫秒的间隔。就像您制作一个按钮来启动相机一样,将以下代码放入其clicked SLOT。

    QTimer *timer = new QTimer;
    timer->setInterval(20);
    connect(timer,SIGNAL(timeout()),this,SLOT(update()));
    

    【讨论】:

      【解决方案3】:

      如果您要在 Label 中启动 Capture Video,那么您必须像在 CPP 中那样编写以下代码: 这段代码对我来说真的很好,我希望它也能帮助你。

      void <Class name Here>::on_button_clicked(){
      captureVideoInLabel.open(0);
          if (captureVideoInLabel.isOpened() == false)
          {
              qDebug() << "Camera can't open";
              return;
          }
          timer = new QTimer(this);
          connect(timer, SIGNAL(timeout()), this, 
      SLOT(processFrameAndUpdateGUI()));
          timer->start(20);
      }
      
      void <here you need to write class name>::processFrameAndUpdateGUI()
      {
          Mat originalImage;
          captureVideoInLabel.read(originalImage);
      
          if (originalImage.empty() == true)
          {
              return;
          }
      
          QImage qOriginalImage((uchar*)originalImage.data, originalImage.cols, 
          originalImage.rows, originalImage.step, QImage::Format_RGB888);
          ui->label->setPixmap(QPixmap::fromImage(qOriginalImage));
      }
      

      在标题中:

      private slots:
          void processFrameAndUpdateGUI();
          void on_button_clicked();
      private:
          cv::VideoCapture captureVideoInLabel;
      

      【讨论】:

      • 欢迎来到 SO。请阅读此how-to-answer 以完善您的答案。只发不带描述的代码是不好的。
      猜你喜欢
      • 2017-02-09
      • 2011-02-05
      • 2020-03-06
      • 1970-01-01
      • 2021-12-19
      • 2023-04-08
      • 2017-09-25
      • 2011-04-15
      相关资源
      最近更新 更多