7

I work on Win7 x64 with OpenCV and Qt Libraries and VS 2010.

I would like to open my camera with OpenCV and then to show captured frames with Qt, for example using QLabel, after converting from Mat to QImage.

I want to do this because to use the functions imshow("camera", image) and waitKey() slows down streaming camera.

This is my code:

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

Webcam is opened correctly and works, But I see a white window and not the captured frames, as you can see in this image enter image description here

If I uncomment: namedWindow (..), imshow(..), if(waitKey(..), it works (i see two windows with the same images), but I show captured frames with OpenCV and this is what I want to avoid.

My question is: I'm wrong in something?? I do not know, the conversion from Mat to Qimage is wrong ??.. Or, cannot I display captured frames only with Qt?

Thank you!

Cristina1986
  • 505
  • 1
  • 8
  • 21

3 Answers3

9

I don't have much experience, but I can see what can go wrong here:

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

You are doing this in dead loop - it will cause your QLabel to update itself infinitely, so you may not see anything. Also, if uncommenting waitKey is helping you, that pretty much mean that you are converting data to QImage well, but something other is broken.

Note that a.exec() will never execute, as you will be stuck in the loop, but I guess this was enough for hitting the concept.

In order not to stuck event loop, you need to create QTimer and every x milliseconds to update your widget:

 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;   
}
Nemanja Boric
  • 21,627
  • 6
  • 67
  • 91
  • Sleeping in the main thread will have the same effect as the for loop. You must not block the main thread and return to the event loop instead. Use a QTimer or a separate thread. – Frank Osterfeld Feb 06 '13 at 13:13
  • Thank you very much for your answer! I tried to copy your code in my file .cpp (called: "main_proiezione.cpp"), but when I execute it, I get this message on terminal: `Object::connect: No such slot QWidget::updatePicture() in c:\ ... `(my "main_proiezione.cpp" file's path). Sorry, I'm not very good with programmation. – Cristina1986 Feb 06 '13 at 13:54
  • I've forgot Q_OBJECT macro in my answer! Try now. – Nemanja Boric Feb 06 '13 at 13:58
  • @Cristina1986 Also, Don't forget to run moc.exe against it if you are compiling it manually without Qt Addin, and move VideoWindow class into separate header file. – Nemanja Boric Feb 06 '13 at 14:05
  • I executed your code with Qt Creator (so I avoide moc problem). It compile perfectly, but show me again a white window (web camera is opened). – Cristina1986 Feb 06 '13 at 15:23
  • @Cristina1986 try to save pixmap to file to see if anything is recorded: `(QPixmap::fromImage(image1)).save( "screenshot.png" );` just above `setPixmap()` – Nemanja Boric Feb 06 '13 at 15:31
  • `save()` function works! but I don't know why I cannot see images into window `w` (it's always white). – Cristina1986 Feb 06 '13 at 16:46
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/24049/discussion-between-cristina1986-and-nemanja-boric) – Cristina1986 Feb 06 '13 at 16:55
  • ok! it works adding `QLabel mylabel` as private variable and adding `mylabel.show()` in `updatePicture()`, after `mylabel.setPixmap()`. – Cristina1986 Feb 06 '13 at 17:23
  • Video streaming is very slow.. I was hoping to speed up the live streaming passing from opencv to Qt, but most probably I'll pass again to opencv that is slow, but faster than qt. Thanks for your time and answers. – Cristina1986 Feb 06 '13 at 17:41
  • 1
    Is there a typo in this code? the line in the constructor that starts connect is missing a bracket at the end. – GPPK May 29 '14 at 13:28
1

Try this in the paintEvent() of the widget (widget/mainwindow) you want to use to display the image.

Open the camera in the constructor of the widget:

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

in the paintEvent() do this

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)

using a timer you can connect the timeout SIGNAL to the update SLOT of the widget. use an interval of 20-40 ms. Like if you make a pushbutton to start the camera, put the following code in its clicked SLOT.

QTimer *timer = new QTimer;
timer->setInterval(20);
connect(timer,SIGNAL(timeout()),this,SLOT(update()));
mskfisher
  • 3,291
  • 4
  • 35
  • 48
rotating_image
  • 3,046
  • 4
  • 28
  • 46
1

If you are setting to start Capture Video in Label then you have to write this code like that in CPP: This code is working really fine for me i hope it will also help you.

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

in Header:

private slots:
    void processFrameAndUpdateGUI();
    void on_button_clicked();
private:
    cv::VideoCapture captureVideoInLabel;
  • Welcome to SO. Please read this [how-to-answer](http://stackoverflow.com/help/how-to-answer) to refine your answer. Only post code without description is not a good one. – thewaywewere May 06 '17 at 19:50