参考 基于Opencv2.4.11+OpenGL(Qt5.6.0)实现增强现实(三)
之前已经实现了在QGLWidget中绘制一个三维物体并实现手动控制旋转,现在整理一下关于opencv调用摄像头的事例。
网上有很多调用摄像头数据的方法,但随着opencv更新到3.1.0版本以上,已经简化了很多,参考《OpenCV3编程入门 毛星云》给出以下事例代码:
#include<opencv2/opencv.hpp>
using namespace cv;
int main()
{
//[1]读入视频
VideoCapture capture(0);
//[2]循环显示每一帧
while(1)
{
Mat frame; //定义一个Mat变量 用于存储每一帧的图像
capture>>frame; //读取当前帧
imshow("读取摄像头视频",frame); //显示当前帧
waitKey(30); //延时30ms
}
return 0;
}
由上段代码可知,先初始化摄像头capture,最好在全局中初始化,而不是在函数中使用,否则会出现摄像头闪烁,重复调用的问题;
其中0打开的是自带的摄像头,1 打开外接的相机 ,也可以直接读取本地视频;开启摄像头后,每一帧的画面会用一个Mat格式的变量储存;
opencv 显示图像可以用imshow直接显示Mat类型的图像;
但若要将图像信息实时显示在QT界面中,需要转换成QImage的格式,并且赋予纹理【多个纹理可用数组储存】,绘制应当在paintGL()函数中进行,注意画面刷新的问题;
#include "demo.h"
#include "opengl.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
DEMO w;
w.show();
return a.exec();
}
#include
#include "ui_demo.h"
#include "opengl.h"
//#include
//#include
class DEMO : public QWidget
{
Q_OBJECT
public:
DEMO(QWidget *parent = 0);
~DEMO();
private slots:
void setParams();
private:
Ui::DEMOClass ui;
};
#endif // DEMO_H
#include "demo.h"
//#include
//
//using namespace cv;//
int xAngle; int yAngle; int zAngle;
DEMO::DEMO(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
}
DEMO::~DEMO()
{
}
void DEMO::setParams()
{
xAngle = ui.horizontalSlider->value();
yAngle = ui.horizontalSlider_2->value();
zAngle = ui.horizontalSlider_3->value();
}
#ifndef OPENGL_H
#define OPENGL_H
#include
#include
#include
class openGL : public QGLWidget { Q_OBJECT public: openGL(QWidget *parent); ~openGL(); QTimer clk; float m_x, m_y, m_z; GLuint textur[2]; QImage tex1, tex2; QImage buf1, buf2; protected: void initializeGL(); void initWidget(); void paintGL(); void resizeGL(int width, int height); void loadGLTextures1(); void loadGLTextures2(); private slots: void updateWindow(); private: // Ui::DEMOClass ui; }; #endif // OPENGL_H #include "opengl.h" #include
#include
using namespace cv; extern int xAngle; extern int yAngle; extern int zAngle; Mat frame; VideoCapture capture(0); openGL::openGL(QWidget *parent) : QGLWidget(parent) { //Camera(); initWidget(); //初始化窗口 initializeGL(); clk.start(5); //30毫秒计时 时间度量 QObject::connect(&clk, SIGNAL(timeout()), this, SLOT(updateWindow())); //发送者 信号 接收者 函数成员 } openGL::~openGL() { } void openGL::initializeGL() //该函数用来初始化当前的 OpenGL 环境 { loadGLTextures1(); //自定义的构造函数 //载入纹理 loadGLTextures2(); glEnable(GL_TEXTURE_2D); // Enable Texture Mapping ( NEW ) glShadeModel(GL_SMOOTH); // Enable Smooth Shading glClearColor(0.0, 0.0, 0.0, 0.0); // Black Background //设置清除颜色 glClearDepth(1.0); // Depth Buffer Setup //指定深度缓冲区中每个像素需要的值 glEnable(GL_DEPTH_TEST); // Enables Depth Testing 开启深度测试 glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations } void openGL::initWidget() { setGeometry(0, 200, 640, 480); //相对于父窗体来说的一种对子窗体进行位置设置的方法 setWindowTitle(tr("opengl demo"));//想要setGeometry有效,控件就不能在布局里。 //如果使用了布局,控件大小有布局自动控制。 //opengl窗口在ui界面中设置过了,所以这两句话没用了 } void openGL::paintGL() //绘制一般在此函数内进行。 { // /////////////////////////////////////////////////////////////////////////////// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 把窗口清除为当前颜色|清除深度缓冲区 glLoadIdentity(); //恢复初始坐标系 //下面这两个函数,不论是平移还是旋转都是针对于上一个矩阵来说的 glTranslatef(0.0, 0.0, -6.0);//平移 glScalef(0.7f, 0.7f, 0.7f); glRotatef(m_x, 1.0, 0.0, 0.0);//旋转 做(0,0,0)到(x,y,z)的向量,用右手握住这条向量,大拇指指向向量的正方向,四指环绕的方向就是旋转的方向; glRotatef(m_y, 0.0, 1.0, 0.0); glRotatef(m_z, 0.0, 0.0, 1.0); //绑定纹理特性 glBindTexture(GL_TEXTURE_2D, textur[0]); //当把一张纹理绑定到一个目标上时,之前对这个目标的绑定就会失效。 //需要注意,一定不能放在glBegin()和glEnd()函数对中 ////////////////////////////////////////////////////// glTexImage2D(GL_TEXTURE_2D,//目标 0, // 级别 3, // 纹理内部格式 tex1.width(), // 纹理的宽(最好2的次方) tex1.height(), // 纹理的高(最好2的次方) 0,// 纹理的深度(最好2的次方) GL_RGBA,// 纹理单元格式(GL_BGRA=0x80E1) GL_UNSIGNED_BYTE, //像素的数据类型 tex1.bits()); // 数据指针 //真正开始创建纹理数据 //当所显示的纹理比加载的要小时,用 GL_LINEAR处理 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //当所显示的纹理比加载的要大时,用 GL_LINEAR处理 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ///////////////////////////////////////////////////////////// glBegin(GL_QUADS);//GLenum mode 创建元素的类型,比如:点,线等 //GL_QUADS: 绘制由四个顶点组成的一组单独的四边形 glNormal3f(0.0, 0.0, 1.0); ////指定参数设置当前的法线向量 //与光照有关 物体是否能被照射 glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 1.0); glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, 1.0); glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, 1.0); glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 1.0); //(GLfloat s, GLfloat t):一个完全纹理的四个顶点的坐标分别是(0.0f, 0.0f)、(0.0f, 1.0f)、(1.0f, 1.0f)、(1.0f, 0.0f) //分别对应左下、左上、右上、右下角 →x↑y //该函数主要与glVertex3f()配合使用,glTexCoord2f()是配置纹理坐标,glVertex3f()是配置图形坐标。 //载入的位图大小必须是M*M,M是2的某次方,如32,64,128等。 glNormal3f(0.0, 0.0, -1.0); glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0); glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, -1.0); glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, -1.0); glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, -1.0); glNormal3f(0.0, 1.0, 0.0); glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0); glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 1.0); glTexCoord2f(1.0, 0.0); glVertex3f(1.0, 1.0, 1.0); glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, -1.0); glNormal3f(0.0, -1.0, 0.0); glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0); glTexCoord2f(0.0, 1.0); glVertex3f(1.0, -1.0, -1.0); glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 1.0); glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0); glNormal3f(1.0, 0.0, 0.0); glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, -1.0); glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, -1.0); glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, 1.0); glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 1.0); glNormal3f(-1.0, 0.0, 0.0); glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0); glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0); glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0); glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0); glEnd(); //////////////////////////// //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 把窗口清除为当前颜色|清除深度缓冲区 glLoadIdentity(); glTranslatef(0.0, 0.0, -50.0);//平移 glScalef(30.35f, 20.35f, 1.0f);//将模型按x,y,z方向分别拉伸几倍;参数也可取负数,也可以理解为先关于某轴翻转180°,再缩放 glBindTexture(GL_TEXTURE_2D, textur[1]); /////////////////////////////////////////////////////////// glTexImage2D(GL_TEXTURE_2D,//目标 0, // 级别 3, // 纹理内部格式 tex2.width(), // 纹理的宽(最好2的次方) tex2.height(), // 纹理的高(最好2的次方) 0,// 纹理的深度(最好2的次方) GL_RGBA,// 纹理单元格式(GL_BGRA=0x80E1) GL_UNSIGNED_BYTE, //像素的数据类型 tex2.bits()); // 数据指针 //真正开始创建纹理数据 //当所显示的纹理比加载的要小时,用 GL_LINEAR处理 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //当所显示的纹理比加载的要大时,用 GL_LINEAR处理 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //////////////////////////////////////////////////////////// glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 1.0); glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, 1.0); glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, 1.0); glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 1.0); glDeleteTextures(1, &textur[1]);//及时释放不然会占用很多内存空间使电脑卡死 glEnd(); } void openGL::resizeGL(int width, int height) //该函数主要用来对高度和宽度进行一些变化处理 { if (0 == height) { height = 1; } glViewport(0, 0, (GLint)width, (GLint)height); //在窗口中定义一个像素矩形,最终将图像映射到这个矩形中 glMatrixMode(GL_PROJECTION); //表示把当前矩阵指定为用于投影变换 //视图矩阵GL_MODELVIEW、投影矩阵:GL_PROJECTION、纹理矩阵GL_TEXTURE //如果乘以变换矩阵(平移, 缩放, 旋转), 那相乘之后, 模型的位置被变换; //如果乘以投影矩阵(将3D物体投影到2D平面), 相乘后, 模型的投影方式被设置; //如果乘以纹理矩阵(), 模型的纹理方式被设置. glLoadIdentity(); // 矩阵设为单位矩阵 GLdouble aspectRatio = (GLfloat)width / (GLfloat)height; GLdouble zNear = 0.1; GLdouble zFar = 100.0; GLdouble rFov = 50.0 * 3.14159265 / 180.0; glFrustum(-zNear * tan(rFov / 2.0) * aspectRatio, zNear * tan(rFov / 2.0) * aspectRatio, -zNear * tan(rFov / 2.0), zNear * tan(rFov / 2.0), zNear, zFar); //void glFrustum(GLdouble left, GLdouble Right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far); //这个函数的参数只定义近裁剪平面的左下角点和右上角点的三维空间坐标,即(left,bottom,-near)和(right,top,-near); //最后一个参数far是远裁剪平面的离视点的距离值,其左下角点和右上角点空间坐标由函数根据透视投影原理自动生成。near和far表示离视点的远近,它们总为正值(near/far 必须>0)。 glMatrixMode(GL_MODELVIEW); //视图矩阵GL_MODELVIEW glLoadIdentity(); } void openGL::updateWindow() { //将ui控制信号赋值给转动角度 m_x = xAngle; m_y = yAngle; m_z = zAngle; loadGLTextures2(); updateGL();//刷新界面 } void openGL::loadGLTextures1() { if (!buf1.load("F:\\AR\\VS\\demo\\leimu.jpg")) //读取图片 { qWarning("load image failed!"); QImage dummy(128, 128, QImage::Format_RGB32); dummy.fill(Qt::white); buf1 = dummy; } tex1 = QGLWidget::convertToGLFormat(buf1); //将Qt图片的格式buf转换成opengl的图片格式tex glGenTextures(1, &textur[0]); //开辟一个纹理内存,内存指向textur } void openGL::loadGLTextures2() { capture >> frame; waitKey(20); cvtColor(frame, frame, CV_BGR2RGB); //将/Mat类型转换成QImage buf2 = QImage((const unsigned char*)frame.data, frame.cols, frame.rows, frame.cols * frame.channels(), QImage::Format_RGB888); tex2 = QGLWidget::convertToGLFormat(buf2); glGenTextures(1, &textur[1]); }