通过Qt opengl不是为了3D绘制,而是为了将视频绘制起来
使用opengl 可以极大降低yuv转rgb的转换开销
1、为什么用QT的opengl
简单,界面可以自动叠加
void paintGL(); // 具体的绘制写在该函数里
void initializeGL(); // 材质初始化
void resizeGL(int width, int height); // 当窗口发生变化(缩放)
QOpenGLFunctions // 不需要手动添加库,直接继承该函数
GLSL是新的语言,通过GLSL与显卡进行交互,GLSL 跑在显卡上
Program用来编译和运行Shader代码,包括与shader的交互
编译和运行shader // shader两部分:顶点和片元
addShaderFromSourceCode // 加入shader代码
bindAttributeLocation // 设置传入的变量, 顶点和坐标
uniformLocation // 获取变量
顶点着色器是针对每个顶点执行一次,用于确定顶点的位置;——三维
片元着色器是针对每个片元(可以理解为每个像素)执行一次,用于确定每个片元(像素)的颜色 ——平面
GLSL基本语法与C基本相同
它完美地支持向量和矩阵操作
GLSL提供了大量的内置函数来提供丰富的拓展功能
它是通过限定符操作来管理输入输出类型
显卡运算能力:值以三角形为单位,所画的数量
顶点着色器被使用在传统的基于顶点的操作, 例如位移矩阵、计算光照方程、产生贴图坐标。
顶点着色器被应用指定, 应用于客户的顶点转化。
在片元着色器阶 段只有唯一的 varying 输出变量- 即内建变量: gl_FragColor(像素点颜色)




glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertexVertices);
ATTRIB_VERTEX:顶点坐标 2 :坐标数量 GL_FLOAT:单位数
0:法线 0:步宽
glEnableVertexAttribArray(ATTRIB_VERTEX);
使生效
glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0, textureVertices);
材质坐标
glEnableVertexAttribArray(ATTRIB_TEXTURE);
varying 顶点与片元共享 // 算出顶点坐标
attribute 顶点使用,由bindAttributeLocation传入
uniform 程序传入 uniformLocation获取地址
glUniform1i(textureUniformY, 0); 设置
attribute vec4 vertexIn; // 顶点输入
attribute vec2 textureIn; // 材质输入
void main(void)
{ gl_Position = vertexIn;
textureOut = textureIn; }
varying vec2 textureOut; //取出材质数值
uniform sampler2D tex_y; // 三个材质
uniform sampler2D tex_u;
uniform sampler2D tex_v;
void main(void)
{
vec3 yuv;
vec3 rgb;
yuv.x = texture2D(tex_y, textureOut).r;
yuv.y = texture2D(tex_u, textureOut).r - 0.5;
yuv.z = texture2D(tex_v, textureOut).r - 0.5;
rgb = mat3(1, 1, 1, 0, -0.39465, 2.03211, 1.13983, -0.58060, 0) * yuv;
gl_FragColor = vec4(rgb, 1);
}
前面通过OpenGLWidget管理整个窗口,最终显示涉及在某个材质上,最终要把ffmpeg数据写入,要考虑如何在材质中写入ffmpeg数据
glGenTextures(1, t); // 创建材质个数,指针地址
glBindTexture(GL_TEXTURE_2D, *t); // 绑定材质类型成2D图像
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 放大、缩小(通过线性插值)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GL_TEXTURE_2D: 操作2D纹理.
GL_TEXTURE_MIN_FILTE: 缩小过滤
GL_TEXTURE_MAG_FILTER: 放大过滤
GL_LINEAR: 线性过滤, 使用距离当前渲染像素中心最近的4个纹素加权平均值.
ps:如果是一个点直接复制四倍的话,会产生马赛克的现象
加权计算的话就比较柔和
glActiveTexture(GL_TEXTURE0); // 激活材质,通过编号
glBindTexture(GL_TEXTURE_2D, id_y); // 绑定
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, pixel_w, pixel_h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, plane[0]); glUniform1i(textureUniformY, 0); // 0层材质,材质可以多层
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // 从0开始绘制,4个
glViewport(0, 0, width, height);
glTexImage2D(GL_TEXTURE_2D, // 在显存中创建纹理
0, //细节 0默认 镜头拉远拉近
GL_RED, //gpu内部格式
videoWidth,
videoHeight
, GL_RED, //数据格式 数据格式和gpu内部格式 要一致
GL_UNSIGNED_BYTE //像素的数据类型
, data);
glTexSubImage2D // 修改纹理
https://blog.csdn.net/jiaolu295/article/details/115898600
#include "XVideoWidget.h"
#include <QDebug>
#include <QTimer>
// 自动加双引号
#define GET_STR(x) #x
#define A_VER 3
#define T_VER 4
FILE *fp = NULL; // 文件接口
// 顶点shader
const char *vString = GET_STR(
attribute vec4 vertexIn; // 顶点坐标
attribute vec2 textureIn; // 材质坐标
varying vec2 textureOut; // 顶点shader和片元shader共享的变量
void main(void)
{
gl_Position = vertexIn;
textureOut = textureIn;
}
);
// 片元shader
const char *tString = GET_STR(
varying vec2 textureOut; // 共享变量
uniform sampler2D tex_y;
uniform sampler2D tex_u;
uniform sampler2D tex_v;
void main(void)
{
vec3 yuv;
vec3 rgb;
yuv.x = texture2D(tex_y, textureOut).r;
yuv.y = texture2D(tex_u, textureOut).r - 0.5;
yuv.z = texture2D(tex_v, textureOut).r - 0.5;
// 用矩阵转换yuv
rgb = mat3(1.0, 1.0, 1.0,
0, -0.39465, 2.03211,
1.13983, -0.58060, 0.0)*yuv;
// 获取输出颜色
gl_FragColor = vec4(rgb, 1.0);
}
);
// 准备yuv数据
// ffmpeg -i v1080.mp4 -t 10 -s 240x128 -pix_fmt yuv420p out240x128.yuv
// -t 10: 时长10秒钟, 指定输出yuv420p
XVideoWidget::XVideoWidget(QWidget *parent)
:QOpenGLWidget(parent)
{
}
XVideoWidget::~XVideoWidget()
{
}
// 初始化opengl
void XVideoWidget::initializeGL()
{
qDebug() << "initializeGL";
// 初始化opengl函数(QOpenGLFunctions继承)函数
initializeOpenGLFunctions();
// 用program加载shader(顶点和片元)脚本
// 片元(像素)shader
qDebug() << program.addShaderFromSourceCode(QGLShader::Fragment, tString);
// 顶点shader
qDebug() << program.addShaderFromSourceCode(QGLShader::Vertex, vString);
// #############################################以上shader已创建好,接下来要与shader进行交互
// 设置顶点坐标的变量
program.bindAttributeLocation("vertexIn", A_VER); // 将变量名称关联到一个索引中,索引可以用一个宏来实现
// 设置材质坐标
program.bindAttributeLocation("textureIn", T_VER);
// 编译shader,打印
qDebug() << "program.link() = " << program.link();
// 绑定shader,打印
qDebug() << "program.bind() = " << program.bind(); // 将opengl 和shader关联起来
// 传递顶点和材质坐标
// 顶点 顶点坐标是三维,但最后一位不传默认为0
static const GLfloat ver[] = {
-1.0f,-1.0f,
1.0f,-1.0f,
-1.0f,1.0f,
1.0f,1.0f,
};
// 材质
static const GLfloat tex[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
// 将坐标写入opengl中
//顶点 位置索引,一个顶点的元素个数(2),存放类型GL_FLOAT,是否有法线向量 0没有 0默认,ver顶点地址
glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
glEnableVertexAttribArray(A_VER); // 使顶点坐标生效
// 材质
glVertexAttribPointer(T_VER, 2, GL_FLOAT, 0, 0, tex);
glEnableVertexAttribArray(T_VER); // 使顶点坐标生效
// 接下来对材质进行处理
// 从shader获取材质
unis[0] = program.uniformLocation("tex_y");
unis[1] = program.uniformLocation("tex_u");
unis[2] = program.uniformLocation("tex_v");
// 创建材质
glGenTextures(3, texs);
// 绑定Y
glBindTexture(GL_TEXTURE_2D, texs[0]);
// 放大过滤,线性插值(要对周边的点进行加权处理,有渐变的效果) GL_NEAREST()临近插值,效率高(当前点直接复制),但是马赛克严重
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 缩小过滤,线性插值
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// 创建材质显卡空间
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
// 绑定U
glBindTexture(GL_TEXTURE_2D, texs[1]);
// 放大过滤,线性插值
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 缩小过滤,线性插值
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// 创建材质显卡空间
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width / 2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
// 绑定V
glBindTexture(GL_TEXTURE_2D, texs[2]);
// 放大过滤,线性插值
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 缩小过滤,线性插值
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// 创建材质显卡空间
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width / 2 , height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
// 分配材质内存空间
datas[0] = new unsigned char[width * height]; // Y
datas[1] = new unsigned char[width * height / 4]; // U
datas[2] = new unsigned char[width * height / 4]; // V
fp = fopen("out240x128.yuv", "rb");
if (!fp) // 读取失败
{
qDebug() << "out240x128.yuv file open failed!";
}
// 启动定时器
QTimer *ti = new QTimer(this);
connect(ti, SIGNAL(timeout()), this, SLOT(update())); // 信号槽 timeout信号 this:当前窗体 更新
ti->start(40); // 25帧,40ms刷新一次
}
// 刷新显示,实现按钮的叠加
void XVideoWidget::paintGL()
{
if (feof(fp)) // 假如到了结尾,移到开头的位置
{
fseek(fp, 0, SEEK_SET);// 循环播放
}
// 读取数据,存放在datas
fread(datas[0], 1, width*height, fp);
fread(datas[1], 1, width*height / 4, fp);
fread(datas[2], 1, width*height / 4, fp);
glActiveTexture(GL_TEXTURE0); // 激活第0层
glBindTexture(GL_TEXTURE_2D, texs[0]); // 把0层 绑定到材质Y的位置 将显卡中创建的材质绑定到0层材质
//修改材质内容(复制内存内容)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[0]); // 再与内存空间datas进行关联
// 与shader uni 变量关联起来
glUniform1i(unis[0],0);
glActiveTexture(GL_TEXTURE0 + 1 ); // 激活第1层
glBindTexture(GL_TEXTURE_2D, texs[1]); // 把1层 绑定到材质U的位置 将显卡中创建的材质绑定到0层材质
//修改材质内容(复制内存内容)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_RED, GL_UNSIGNED_BYTE, datas[1]); // 再与内存空间datas进行关联
// 与shader uni 变量关联起来
glUniform1i(unis[1], 1);
glActiveTexture(GL_TEXTURE0 + 2); // 激活第2层
glBindTexture(GL_TEXTURE_2D, texs[2]); // 把2层 绑定到材质V的位置 将显卡中创建的材质绑定到0层材质
//修改材质内容(复制内存内容)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_RED, GL_UNSIGNED_BYTE, datas[2]); // 再与内存空间datas进行关联
// 与shader uni 变量关联起来
glUniform1i(unis[2], 2);
// 开始画
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // 从0 开始 画4个点
qDebug() << "paintGL";
}
// 窗口尺寸变化
void XVideoWidget::resizeGL(int width, int height)
{
qDebug() << "resizeGL"<< width<< height;
}
#pragma once
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QGLShaderProgram>
class XVideoWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
XVideoWidget(QWidget *parent);
~XVideoWidget();
protected:
//重载三个函数
// 刷新显示,实现按钮的叠加
void paintGL();
// 初始化gl
void initializeGL();
// 窗口尺寸变化
void resizeGL(int width, int height);
private:
// shader程序,通过program运行
QGLShaderProgram program;
// shader中yuv变量地址
GLuint unis[3] = { 0 };
// opengl的 texture 地址
GLuint texs[3] = { 0 };
// 材质的内存空间
unsigned char *datas[3] = { 0 };
int width = 240;
int height = 128;
};
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="
假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit
在前面两节的例子中,主界面窗口的尺寸和标签控件显示的矩形区域等,都是用C++代码编写的。窗口和控件的尺寸都是预估的,控件如果多起来,那就不好估计每个控件合适的位置和大小了。用C++代码编写图形界面的问题就是不直观,因此Qt项目开发了专门的可视化图形界面编辑器——QtDesigner(Qt设计师)。通过QtDesigner就可以很方便地创建图形界面文件*.ui,然后将ui文件应用到源代码里面,做到“所见即所得”,大大方便了图形界面的设计。本节就演示一下QtDesigner的简单使用,学习拖拽控件和设置控件属性,并将ui文件应用到Qt程序代码里。使用QtDesigner设计界面在开始菜单中找到「Q
动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、
2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p
Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图
在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.
我正在尝试创建一个带有项目符号字符的Ruby1.9.3字符串。str="•"+"helloworld"但是,当我输入它时,我收到有关非ASCII字符的语法错误。我该怎么做? 最佳答案 你可以把Unicode字符放在那里。str="\u2022"+"helloworld" 关于ruby-如何在Ruby字符串中插入项目符号字符?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1195