QT与openCV,与PCL结合!

(1):详解QT多媒体框架:给予视频播放器

原文链接:http://mobile.51cto.com/symbian-271123.htm

   

   对于使用主框架的QT程序,实现Qimage的转换可借鉴下面程序段:

void MainWindow::on_openButton_clicked()
{
    //第3个参数表示查找文件时从哪个目录开始,如果为"."的话,表示从该工程目录开始查找,最后那个参数的过滤器的名字之间
    //要用空格,否则识别不出来
    QString img_name = QFileDialog::getOpenFileName( this, tr("Open Image"), ".",
                                                     tr("Image Files(*.png *.jpg *.jpeg *.bmp)"));
    //toAscii()返回8位描述的string,为QByteArray,data()表示返回QByteArray的指针,QByteArray为字节指针
    Mat src = imread( img_name.toAscii().data() );
    cvtColor( src, src, CV_BGR2RGB );
    QImage img = QImage( (const unsigned char*)(src.data), src.cols, src.rows, QImage::Format_RGB888 );
//    namedWindow( "Image_Show", WINDOW_AUTOSIZE );
//    imshow( "Image_Show", src );
    ui->label->setPixmap( QPixmap::fromImage(img) );
 //   ui->label->resize( ui->label->pixmap()->size() );

}

(2):详解QT 快速应用OPENCV ,图片示例:基于widget!!!

原文链接:http://mobile.51cto.com/symbian-271260.htm


QT 框架中快速应用OpenCV 是本文要结束的内容,和MFC比较起来,QT的信号槽机制比MFC的信号机制慢,但是因为能很好的实现跨平台,所以我在这里总结一下可能对一些人有点用。OpenCV.China论坛上有一个帖子叫做《在MFC框架中快速应用OpenCV》看了后就想结合QT写一下。

0搭建环境:OpenCV + QT 4.6

我的实验是基于VS2008来做的,QT官方虽然提供了VS2008-add-in的插件,我没有用。直接下载器编译好的库文件进行配置,OpenCV的在VS2008下面的配置方法Google一下到处都是,这里不再补充。首先需要做的是在VS2008里面你需要使QT下和OpenCV的程序能分别跑起来。对于QT在VS的配置其实挺简单,有头文件和相应的链接库,保证调用的时候路径正确,一般就没有问题了。常用命令行make程序的人应该会很清楚那些IDE只不过是层画皮。

.pro配置:

INCLUDEPATH += d:\opencv\build\include\  
INCLUDEPATH += d:\opencv\build\include\opencv\  
INCLUDEPATH += d:\opencv\build\include\opencv2\  
  
CONFIG(debug,debug|release) {  
LIBS += -Ld:\opencv\build\x86\vc10\lib \  
    -lopencv_core243d \  
    -lopencv_highgui243d \  
    -lopencv_imgproc243d \  
    -lopencv_features2d243d \  
    -lopencv_calib3d243d  
} else {  
LIBS += -Ld:\opencv\build\x86\vc10\lib \  
    -lopencv_core243 \  
    -lopencv_highgui243 \  
    -lopencv_imgproc243 \  
    -lopencv_features2d243 \  
    -lopencv_calib3d243  
}  
1.显示图像

QWidget是QObject下的第一个子类,使用它显示图像会减少不必要的开销。首先定制一个自己需要的QWidget:

  1. class myWidget : public QWidget  
  2. {  
  3.   Q_OBJECT  
  4.   public:  
  5.     myWidget(const IplImage *img,QWidget *parent = 0);  
  6.     ~myWidget();  
  7.   protected:  
  8.     void paintEvent(QPaintEvent *e);  
  9.   private:  
  10.     IplImage* iplImg;  
  11.     QImage *qImg;  
  12. }; 

需要绘制一个图像,我重载paintEvent(QpaintEvent *e),我在这里面使用QPainter进行绘制。

  1. void myWidget::paintEvent(QPaintEvent *e)  
  2. {  
  3.     QPainter painter(this);  
  4.     painter.drawImage(QPoint(5,5),*qImg);  

rawImage(QPoint(5,5),qImg);的作用是将qImg绘制在左上顶点位于QPoint(5,5)处。

这里面有可能两个问题,第一个问题是要显示的图片太小,创建的Widget太大,最后显示比较丑陋。这时可以在此函数里面获得qImg的宽高,然后resize一下就好了。另外一个问题是:绘制的时候使用的是QImage,不是IplImage类型。关于这个问题论坛上有人专门写了IplImage <-> QImage的转换代码,我在这里不重复那个做法,一是有人已经做了,另外处于效率考虑,这里提供另一种方法。

通常同学们都是用cvLoadImage来读图片,保存在IplImage里面,在这里这个图片我们保存在img里面,然后通过img传进QWidget,然后我new一个QImage

  1. qImg = new QImage(QSize(img->width,img->height),QImage::Format_RGB888); 

我这里假设iplImg是RGB格式,且每个通道大小为8。然后创建一个IplImage 的文件头

  1. iplImg = cvCreateImageHeader(cvSize(img.width(),img.height()),8,3); 

此iplImage和QImage的不同之处在于QImage没有直接提供创建文件头的方法,可以通过如下方式创建只有文件头数据的QImage

  1. qImg = new QImage(QSize(0,0),QImage::Format_RGB888); 

另外两者的图像矩阵像素排列有点不同,比如IplImage中的BGR到了QImage中应该是RGB,当然单通道的灰度图是一样的,值得庆幸的是两者的像素矩阵都是形状相同的多维数组。这样我们可以通过指针共享这部分数据,一种方法如下:

  1. iplImg->imageData = (char*)qImg.bits(); 

将iplImg的图像矩阵指到qImg那里,以后我们只需要对IplImage运用opencv里面的函数进行处理,其实就直接在处理qImg里面的数据了。但是现在的图像数据还在img里面,首先得把数据搞到手,然后放到iplImg和qImg的共享区中去,另外将颜色排列以QImage中的RGB顺序为标准。

  1. if (img->origin == IPL_ORIGIN_TL)  
  2. {  
  3.  cvCopy(img,iplImg,0);  
  4. }  
  5. else  
  6. {  
  7.  cvFlip(img,iplImg,0);  
  8. }  
  9. cvCvtColor(iplImg,iplImg,CV_BGR2RGB); 

实际上只要做到这里图片就能显示了。如下图所示

QT 框架中快速应用OpenCV 基于图片

给出myWidget.cpp完整代码

  1. #include "myWidget.h"  
  2. #include <QtGui\QPainter> 
  3. #include <QtCore\QPoint> 
  4.  
  5. myWidget::myWidget(const IplImage *img,QWidget *parent /* = 0 */) : QWidget(parent)  
  6. {  
  7.   qImg = new QImage(QSize(img->width,img->height),  
  8.    QImage::Format_RGB888);  
  9.  iplImg = cvCreateImageHeader(cvSize(img->width,img->height),  
  10.   8,3);  
  11.  iplImg->imageData = (char*)qImg->bits();  
  12.  
  13.  if (img->origin == IPL_ORIGIN_TL)  
  14.  {  
  15.   cvCopy(img,iplImg,0);  
  16.  }  
  17.  else  
  18.  {  
  19.   cvFlip(img,iplImg,0);  
  20.  }  
  21.  cvCvtColor(iplImg,iplImg,CV_BGR2RGB);  
  22.  this->resize(img->width,img->height);  
  23. }  
  24. myWidget::~myWidget()  
  25. {  
  26.  cvReleaseImage(&iplImg);  
  27.  delete qImg;  
  28. }  
  29. void myWidget::paintEvent(QPaintEvent *e)  
  30. {  
  31.  QPainter painter(this);  
  32.  painter.drawImage(QPoint(0,0),*qImg);  

调用的代码很简单:

  1. int main(int argc,char* argv[])  
  2. {  
  3.  QApplication app(argc,argv);  
  4.  
  5.  IplImage *img = cvLoadImage("460.jpg",1);  
  6.  if (img)  
  7.  {  
  8.   myWidget *mw = new myWidget(img);  
  9.   mw->show();  
  10.  }  
  11.  int re = app.exec();  
  12.  cvReleaseImage(&img);  
  13.  return re;  

小结:关于详解 QT 框架中快速应用OpenCV 上篇内容介绍完了




(3):利用Qt与OpenCV简单实现摄像头图像捕捉

原文链接:http://blog.const.net.cn/a/8763.htm


为了pro工程文件看起来简单点,创建一个pri文件,包含opencv相关信息。分别为VS2008、Mingw和Linux设置OpenCV头文件和库的路径。OPENCVPATH_MSVC=D:/Qt/OpenCV-2.1....


为了pro工程文件看起来简单点,创建一个 pri 文件,包含 opencv相关信息。

分别为 VS2008、Mingw和Linux设置OpenCV头文件和库的路径。

OPENCVPATH_MSVC = D:/Qt/OpenCV-2.1.0-vs2008

win32-msvc*{
     INCLUDEPATH += $${OPENCVPATH_MSVC}/include
     CONFIG(debug, debug|release) {
         LIBS+=-L$${OPENCVPATH_MSVC}/lib/debug 
               -lcxcore210d 
               -lhighgui210d
     } else {
         LIBS += -L$${OPENCVPATH_MSVC}/lib/release 
                 -lcxcore210 
                 -lhighgui210
    }
}

OPENCVPATH_MINGW = D:/Qt/OpenCV-2.1.0-mingw
win32-g++{
    INCLUDEPATH += $${OPENCVPATH_MINGW}/include
    LIBS += -L$${OPENCVPATH_MINGW}/lib 
            -lcxcore210 
            -lhighgui210
}

unix:LIBS += -lcv 
    -lhighgui

读取类

创建一个视频捕捉类,提供start和stop槽来控制摄像头打开和关闭,通过定时器定时捕捉图像,并通过信号将图片传出。

//cameradevice.h
#ifndef CAMERADEVICE_H
#define CAMERADEVICE_H

#include <QtCore/QObject>

QT_BEGIN_NAMESPACE
class QTimer;
class QImage;
QT_END_NAMESPACE

namespace cv{
    class VideoCapture;
    class Mat;
}

class CameraDevice : public QObject
{
    Q_OBJECT
public:
    explicit CameraDevice(QObject *parent = 0);
    ~CameraDevice();

signals:
    void imageReady(const QImage& image);

public slots:
    bool start();
    bool stop();

private slots:
    void onTimeout();

private:
    QImage imageFromMat(const cv::Mat& frame);
    cv::VideoCapture * m_capture;
    QTimer * m_timer;
};

#endif // CAMERADEVICE_H
//cameradevice.cpp
#include <QtCore/QTimer>
#include <QtGui/QImage>
#include "opencv/cv.h"
#include "opencv/highgui.h"
#include "cameradevice.h"

CameraDevice::CameraDevice(QObject *parent) :
    QObject(parent)
{
    m_capture = new cv::VideoCapture;
    m_timer = new QTimer(this);

    connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
}

CameraDevice::~CameraDevice()
{
    delete m_capture;
    m_capture = NULL;
}

bool CameraDevice::start()
{
    if (m_capture->isOpened()) {
        return true;
    }
    m_capture->open(CV_CAP_ANY);
    if (m_capture->isOpened()) {
        m_timer->start(40);
    }
    return m_capture->isOpened();
}

bool CameraDevice::stop()
{
    if (m_capture->isOpened()) {
        m_capture->release();
    }
    return true;
}

void CameraDevice::onTimeout()
{
    if (!m_capture->isOpened()) {
        return;
    }
    static cv::Mat frame;
    *m_capture >> frame;
    if (frame.cols) {
        emit imageReady(imageFromMat(frame));
    }
}

QImage CameraDevice::imageFromMat(const cv::Mat &frame)
{
    const unsigned char* src = frame.ptr();
    QImage image(frame.cols, frame.rows, QImage::Format_RGB32);
    unsigned char * des = image.bits();
    unsigned long count = frame.cols * frame.rows;
    for (unsigned long i = 0; i < count; ++i)
    {
        *des++ = *src++;
        *des++ = *src++;
        *des++ = *src++;
        *des++ = 0xff;
    }
    return image;
}

界面类

对话框类就简单了,只需要一个 label 来显示图像,两个按钮来控制开启与关闭即可

//dialog.h
#ifndef DIALOG_H
#define DIALOG_H

#include <QtGui/QDialog>

namespace Ui {
    class Dialog;
}

class CameraDevice;

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private slots:
    void onImageArrival(const QImage & image);

private:
    Ui::Dialog *ui;
    CameraDevice * m_camera;
};

#endif // DIALOG_H[喝小酒的网摘]http://blog.const.net.cn/a/8763.htm
//dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include "cameradevice.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog),
    m_camera(new CameraDevice(this))
{
    ui->setupUi(this);

    connect(m_camera, SIGNAL(imageReady(QImage)), this, SLOT(onImageArrival(QImage)));
    connect(ui->startButton, SIGNAL(clicked()), m_camera, SLOT(start()));
    connect(ui->stopButton, SIGNAL(clicked()), m_camera, SLOT(stop()));
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::onImageArrival(const QImage &image)
{
    ui->view->setPixmap(QPixmap::fromImage(image));
}

主程序

//main.cpp
#include <QtGui/QApplication>
#include "dialog.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Dialog w;
    w.show();

    return a.exec();
}

问题

用一个定时器来做似乎存在些问题,反复打开关闭,在windows下可发现资源泄漏。不清楚是什么原因。

简单调整几行代码,改为多线程程序后,该问题似乎消失了。

  • 改动主要在对话框类中:
#include <QtCore/QThread>
#include "dialog.h"
#include "ui_dialog.h"
#include "cameradevice.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog),
    m_camera(new CameraDevice)
{
    ui->setupUi(this);

    QThread * thread = new QThread(this);
    m_camera->moveToThread(thread);
    thread->start();

    connect(m_camera, SIGNAL(imageReady(QImage)), this, SLOT(onImageArrival(QImage)));
    connect(ui->startButton, SIGNAL(clicked()), m_camera, SLOT(start()));
    connect(ui->stopButton, SIGNAL(clicked()), m_camera, SLOT(stop()));
}

Dialog::~Dialog()
{
    m_camera->deleteLater();
    delete ui;
}

void Dialog::onImageArrival(const QImage &image)
{
    ui->view->setPixmap(QPixmap::fromImage(image));
}

改为多线程后,可将捕捉类中的定时器的时间设为 0





(4):QThread的使用方法


昨天不小心看到Qt开发人员( Bradley T. Hughes)Blog中的一片文章 you are-doing-it-wrong 。 结果看得头昏脑胀:好歹也自学了近1年的Qt,也一直很小心、很认真地阅读Qt和manual和例子等资料,却被突然告知,QThread的正确使用方法 是一种自己从没见过,而且Qt manual、example、书籍中都没有提到过的一种方法。到底怎么了... [喝小酒的网摘]http://blog.const.net.cn/a/8760.htm

莫非manual、exmaple以及资料中的介绍都是错的??

认真看看其他的人的评论,总算理清了一点头绪。所有事情源于 QThread 的事件循环!


QThread 的两种使用方法


1. 不使用事件循环。这是官方的 Manual 、example 以及相关书籍中都介绍的一种的方法。
a. 子类化 QThread
b. 重载 run 函数,run函数内有一个 while 或 for 的死循环
c. 设置一个标记为来控制死循环的退出。

2. 使用事件循环。(博客 you are-doing-it-wrong 批驳的就是这种情况下的 一种用法。)

a. 子类化 QThread,
b. 重载 run 使其调用 QThread::exec() 
c. 并为该类定义信号和槽,这样一来,由于槽函数并不会在新开的 thread 运行,很多人为了解决这个问题在构造函数中调用 moveToThread(this); 

而争论和不解正是这样的一条语句造成的。

Bradley T. Hughes 给出说明是 QThread 应该被看做是操作系统线程的接口或控制点,而不应该包含需要在新线程中运行的代码。需要运行的代码应该放到一个QObject的子类中,然后将该子类的对象moveToThread到新线程中。

另外:
在Qt4.3(包括)之前,run 是虚函数,必须子类化QThread来实现run函数。
而从Qt4.4开始,qthreads-no-longer-abstract    ,run 默认调用 QThread::exec() 。这样一来不需要子类化 QThread 了,只需要子类化一个 QObject 就够了,这正是被 Bradley T. Hughes推荐的方法。


终于看懂了,但
不管怎么说,都应该是 QThread 当初的设计导致的这种问题,而所有文档和例子中都没有提到该如何使用Qthread 进一步加剧了对QThread的这种误用。

相关链接:

http://labs.qt.nokia.com/blogs/2010/06/17/youre-doing-it-wrong/
http://labs.qt.nokia.com/blogs/2006/12/04/threading-without-the-headache/
http://labs.qt.nokia.com/blogs/2007/07/05/qthreads-no-longer-abstract/
http://gitorious.org/qthreadhowto/qthreadhowto/trees/master
http://blog.exys.org/entries/2010/QThread_affinity.html
http://thesmithfam.org/blog/2010/02/07/talking-to-qt-threads/[喝小酒的网摘]http://blog.const.net.cn/a/8760.htm



(5):使用opencv和QT实现摄像头采集


在网上收罗了很多资料,QT没有专门操作摄像头的类,这个得自己写。网上也有很多关务openCV和V4l的一些介绍,由于我项目要在window下开发,所以就选择了openCV。由于以前没有用过openCV,所以就只看了关于openCVS摄像头操作的这部分,其他的还没时间去看。

openCV : http://sourceforge.net/projects/opencvlibrary/files/opencv-win/2.1/  

刚开始下载的是2.3.1的,安装后发现没有lib库所以后面选择了2.1的

openCV中文学习 pdf  :

现在,开始详细的介绍如何在QT中实时的采集摄像头数据。

打开QTcreator (我用的是QT 2.3的 中文版)

新建一个widget工程


界面上放两个label 分别用来显示摄像头采集到的数据和照的照片。




编辑camaraget.h 文件

    #ifndef CAMARAGET_H   
    #define CAMARAGET_H   
      
    #include <QWidget>   
    #include <QImage>   
    #include <QTimer>     // 设置采集数据的间隔时间   
      
    #include <highgui.h>  //包含opencv库头文件   
    #include <cv.h>   
      
    namespace Ui {  
        class camaraGet;  
    }  
      
    class camaraGet : public QWidget  
    {  
        Q_OBJECT  
      
    public:  
        explicit camaraGet(QWidget *parent = 0);  
        ~camaraGet();  
      
    private slots:  
        void openCamara();      // 打开摄像头   
        void readFarme();       // 读取当前帧信息   
        void closeCamara();     // 关闭摄像头。   
        void takingPictures();  // 拍照   
      
    private:  
        Ui::camaraGet *ui;  
        QTimer    *timer;  
        QImage    *imag;  
        CvCapture *cam;// 视频获取结构, 用来作为视频获取函数的一个参数   
        IplImage  *frame;//申请IplImage类型指针,就是申请内存空间来存放每一帧图像   
    };  
      
    #endif // CAMARAGET_H  


编辑camaraget.cpp

    #include "camaraget.h"   
    #include "ui_camaraget.h"   
      
    camaraGet::camaraGet(QWidget *parent) :  
        QWidget(parent),  
        ui(new Ui::camaraGet)  
    {  
        ui->setupUi(this);  
      
        cam     = NULL;  
        timer   = new QTimer(this);  
        imag    = new QImage();         // 初始化   
      
        /*信号和槽*/  
        connect(timer, SIGNAL(timeout()), this, SLOT(readFarme()));  // 时间到,读取当前摄像头信息   
        connect(ui->open, SIGNAL(clicked()), this, SLOT(openCamara()));  
        connect(ui->pic, SIGNAL(clicked()), this, SLOT(takingPictures()));  
        connect(ui->closeCam, SIGNAL(clicked()), this, SLOT(closeCamara()));  
    }  
      
    /****************************** 
    ********* 打开摄像头 *********** 
    *******************************/  
    void camaraGet::openCamara()  
    {  
        cam = cvCreateCameraCapture(0);//打开摄像头,从摄像头中获取视频   
      
        timer->start(33);              // 开始计时,超时则发出timeout()信号   
    }  
      
    /********************************* 
    ********* 读取摄像头信息 *********** 
    **********************************/  
    void camaraGet::readFarme()  
    {  
        frame = cvQueryFrame(cam);// 从摄像头中抓取并返回每一帧   
        // 将抓取到的帧,转换为QImage格式。QImage::Format_RGB888不同的摄像头用不同的格式。   
        QImage image((const uchar*)frame->imageData, frame->width, frame->height, QImage::Format_RGB888);  
        ui->label->setPixmap(QPixmap::fromImage(image));  // 将图片显示到label上   
    }  
      
    /************************* 
    ********* 拍照 *********** 
    **************************/  
    void camaraGet::takingPictures()  
    {  
        frame = cvQueryFrame(cam);// 从摄像头中抓取并返回每一帧   
      
        // 将抓取到的帧,转换为QImage格式。QImage::Format_RGB888不同的摄像头用不同的格式。   
        QImage image((const uchar*)frame->imageData, frame->width, frame->height, QImage::Format_RGB888);  
      
        ui->label_2->setPixmap(QPixmap::fromImage(image));  // 将图片显示到label上   
    }  
      
    /******************************* 
    ***关闭摄像头,释放资源,必须释放*** 
    ********************************/  
    void camaraGet::closeCamara()  
    {  
        timer->stop();         // 停止读取数据。   
      
        cvReleaseCapture(&cam);//释放内存;   
    }  
      
    camaraGet::~camaraGet()  
    {  
        delete ui;  
    }  

好了,全部代码都OK了(当然,创建工程时,会生成main.cpp,不必去改动它),但现在你点运行,依然会产生错误,为什么呢?因为还没有把openCV的库包含进去。

在*.pro 文件中 加入:

INCLUDEPATH+=C:\OpenCV2.1\include\opencv

	LIBS += C:\OpenCV2.1\lib\highgui210.lib \
 	 C:\OpenCV2.1\lib\cxcore210.lib \
 	 C:\OpenCV2.1\lib\cv210.lib
OK,大功告成,运行后,在widget中点击打开摄像头,就可以看到自己了。运行后的效果:




后来我发现这个效果不怎么好,就改了一下:改了以后的运行效果也贴出来:


就改了一句:

  1. QImage image((const uchar*)frame->imageData, frame->width, frame->height, QImage::Format_RGB888);  
  1. 改为了 QImage image = QImage((const uchar*)frame->imageData, frame->width, frame->height, QImage::Format_RGB888).rgbSwapped();  




(6):求道勿远,求心勿近 的一个博客:Qt Opencv 在Linux下摄像头简单示例v1.0

   原文链接:http://blog.chinaunix.net/uid-24641004-id-3459705.html


下面写的文章也许网上也有类似的,但是大多数都没有给出思路及背景,让初学者每次都只能学到一点皮毛,不少知识需要大量搜索零碎地拼凑起来。题外话,虽然现在是碎片化信息时代,但正是这样信息整合能力也显得非常重要,为读者提供高质量的文章是我以后的目标。我以后会注意分析应用背景及些过程的解析。

不想看下面笔者对编译环境的理解的,可以直接在本页Ctrl+F查找“编译过程”看,因为下面有很大段笔者心酸的回忆,嘻嘻。

资源介绍

opencv是一个非常优秀的图形图像处理类库,里面的类或者结构体,封装了很多实用的图像处理算法,调用其提供的API,等于使用一些复杂的图像处理算法,真是解放生产力啊。这里笔者用的是linux版本的opencv2.2源码,几十M,稳定版本够用即可,没必要时刻追着新版本。不过在linux上的opencv各种版本都是如笔者那样编译即可用。

思考一下,为什么我们要下源码来编译?不是像windows上的直接发布一个压缩包即可用么?我们这次编译为是得到什么?其实很简单,opencv里有专门处理摄像头的API,我们要用它,就得有它的头文件及动态库(或静态库)。而我们拿到源码往往不是为了去研究其代码(如果是做这算法方面的研究便是例外了),而是拿到“本地”编译。至于为何,笔者认为是大家内核版本版本的不统一,编译器版本也不统一导致这样做的的。如果使用Ubuntu之类的,很简单,一个apt-get什么都不用管,因为Ubuntu提供了适应你的内核版本编译器编译的库。知道的同学希望指教一下。

下载请移步:http://www.opencv.org.cn/index.php/Download

虚拟机上的摄像头,一般都是用USB连接,如果是笔记本自带的,也是把虚拟机里的connect上即可。连接外设的方法:在虚拟机顶栏菜单,VM->Removable Devices->XX WebCam->connect  && show in status Bar(前者把设备从win环境解除,连接到虚拟机环境,后者是显示在虚拟机右下角的状态栏)。

具体看下图:

   QT,版本其实与opencv版本没有什么关系,因为代码编译出来,最终都是翻译成机器码,QT源码是C++写的,OPENCV源码也是C/C++写的,编译器认识他们即可。笔者一起也是担心版本问题,网上的文章往往都是写着qt4.7.2+opencv2.0XXX,或者qtcreator2.0+opencv2.3.1在Ubuntu11上编译成功之类的文章,我那时就很担心,又要确定QT版本,又要确定opencv版本,还得确定linux版本吗?linux怎么那么复杂啊?就是他们的标题及其内容都没有明确指出他们方法的通用性。笔者在这里就说明一下:这文章在linux上的适用,QT版本(4.6,4.7,5.0等无论什么版本都可以,whatever),opencv版本(1.0,2.0,2.2,2.3,2.4,whatever都可以),linux(各种发行版本(内核是2.6以上的),只要别用2.4的内核就好。)

编译过程:

摄像头在虚拟机上(当然,从物理上说你还是得有摄像头才行)安装上面的图看一下即可。下面主要是opencv的编译:主要参考opencv中文论坛的文章

  1. ./configure//头文件和库文件一般都是默认安装到/usr/local/include/usr/local/lib内

  2. make

  3. make install

具体代码:

先讲最终要实现什么,很简单,一个窗口,里面有一个label显示摄像头的图像。

然后讲讲原理:每隔一段时间我们就去摄像头抓取一帧图像,然后放到ui->label上面,如果取的时间快,就造成“视频”的感觉了。(可以想到,其实摄像头拍照也是很简单,我们就把其中一帧取出来即可。)

新建一个工程,继承QDialog(用什么窗体都可以),在UI上拖出一个label放在中间,

拉到适当大小。

在工程xxx.pro里面添加必要的库及头文件的路径:


 

 INCLUDEPATH +=/usr/local/include/opencv
    LIBS +=/usr/local/lib/libcv.so\
            /usr/local/lib/libhighgui.so\
            /usr/local/lib/libcxcore.so\

//dialog.h

    #ifndef DIALOG_H
    #define DIALOG_H
    #include <QDialog>
    #include <cv.h>
    #include <highgui.h>
    #include <QTimer>
    #include <QPixmap>
    namespace Ui {
        class Dialog;
    }
    class Dialog :public QDialog
    {
        Q_OBJECT
    public:
        explicit Dialog(QWidget*parent= 0);
        ~Dialog();
    private:
        Ui::Dialog*ui;
        CvCapture *capture;//highgui 里提供的一个专门处理摄像头图像的结构体
        IplImage *frame;//摄像头每次抓取的图像为一帧,使用该指针指向一帧图像的内存空间
        QTimer *timer;//定时器用于定时取帧,上面说的隔一段时间就去取就是用这个实现。
    private slots:
        void getFrame();//实现定时从摄像头取图并显示在label上的功能。
    };
    #endif // DIALOG_H

//dialog.cpp

    #include "dialog.h"
    #include "ui_dialog.h"
    #include <QDebug>
    Dialog::Dialog(QWidget*parent):
        QDialog(parent),
        ui(new Ui::Dialog)
    {
        ui->setupUi(this);
        timer = new QTimer(this);
        capture = cvCaptureFromCAM(0);//cvCaptureFromCAM其实是一个宏,就是cvCreateCameraCapture的别名,0代表第一个摄像头。-1代表默认摄像头。
        if(capture==NULL){
            qDebug()<<"error!";
        }
        timer->start(50);//1000为1秒,50毫秒去取一帧
        connect(timer,SIGNAL(timeout()),this,SLOT(getFrame()));//超时就去取
    }
    void Dialog::getFrame()
    {
        frame = cvQueryFrame(capture);//从摄像头取帧
        QImage image = QImage((const uchar*)frame->imageData, frame->width, frame->height, QImage::Format_RGB888).rgbSwapped();//简单地转换一下为Image对象,rgbSwapped是为了显示效果色彩好一些。
        ui->label->setPixmap(QPixmap::fromImage(image));
    }
    Dialog::~Dialog()
    {
        timer->stop();//停止取帧
        cvReleaseCapture(&capture);//释放资源是个好习惯
        delete ui;
    }

本文参考资料:

源码编译:

http://www.opencv.org.cn/index.php/%E6%BA%90%E7%A0%81%E7%BC%96%E8%AF%91

QT中使用OPENCV库

http://www.opencv.org.cn/index.php/%E5%9C%A8Qt%E4%B8%AD%E4%BD%BF%E7%94%A8OpenCV%E5%BA%93

Debian/Ubuntu

http://www.opencv.org.cn/index.php/Debian%E4%B8%8B%E5%AE%89%E8%A3%85



posted @ 2013-10-20 13:42  wishchin  阅读(1614)  评论(0编辑  收藏  举报