从 AVFrame 中取出帧(YUV)保存为 Mat 格式
由于 cnblogs 不支持科学公式,完整内容请移步原文链接
原文地址:从 AVFrame 中取出帧(YUV)保存为 Mat 格式
从 AVFrame 中取出帧(YUV)保存为 Mat 格式
本文档针对 YUV420p 编码进行记录
AVFrame 结构体解析
这里列出一些重点变量
| 变量定义 | 用途 | 备注 |
|---|---|---|
| uint8_t *data[AV_NUM_DATA_POINTERS] | 解码后原始数据 | |
| int linesize[AV_NUM_DATA_POINTERS] | data中“一行”数据的大小 | 一般大于图像的宽 |
| int width, height | 视频帧宽和高 | |
| int nb_samples | 一个 AVFrame 中包含多少个音频帧 | 一个 AVFrame 中只包含一个视频帧,但可能包含多个音频帧 |
| int format | 解码后原始数据类型 | YUV420, YUV422, RGB24... |
| int key_frame | 是否是关键帧 | |
| enum AVPictureType pict_type | 帧类型 | I, B, P... |
| AVRational sample_aspect_ratio | 宽高比 | 16:9, 4:3... |
| int64_t pts | 显示时间戳 | |
| int coded_picture_number | 编码帧序号 | |
| int display_picture_number | 帧序号 | |
| int interlaced_frame | 是否是隔行扫描 |
YUV 转 RGB 原理
YUV 图像有两种编码格式:
紧缩格式(packed formats): Y、U、V 三通道像素值依次排列,即 Y0 U0 V0 Y1 U1 V1 ...
平面格式(planar formats): 先排列 Y 的所有像素值,再排列 U,最后排列 V
YUV420p 中使用平面格式,水平 2:1 取样,垂直 2:1 采样,即每 4 个 Y 分量对应一个 U、V 分量
综上,YUV 与 RGB 的转换公式如下
R = Y + 1.4075 * (V - 128)
G = Y - 0.3455 * (U - 128) - (0.7169 * (V - 128))
B = Y + 1.7790 * (U - 128)
Y = R * .299000 + G * .587000 + B * .114000
U = R * -.168736 + G * -.331264 + B * .500000 + 128
V = R * .500000 + G * -.418688 + B * -.081312 + 128
下面的公式中各个符号都带了 ',表示该符号在原值基础上进行了伽马校正,伽马校正有助于弥补在抗锯齿的过程中,线性分配伽马值所带来的细节损失,使图像细节更加丰富。在没有采用伽马校正的情况下,暗部细节不容易显现出来,而采用了这一图像增强技术以后,图像的层次更加清晰
Y' = 0.257*R' + 0.504*G' + 0.098*B' + 16
Cb' = -0.148*R' - 0.291*G' + 0.439*B' + 128
Cr' = 0.439*R' - 0.368*G' - 0.071*B' + 128
R' = 1.164*(Y’-16) + 1.596*(Cr'-128)
G' = 1.164*(Y’-16) - 0.813*(Cr'-128) - 0.392*(Cb'-128)
B' = 1.164*(Y’-16) + 2.017*(Cb'-128)
实现代码
在 AVFrame2Img 中输入需要转换格式的 AVFrame,返回 Mat
Mat AVFrame2Img(AVFrame *frame) {
// 获取帧宽、高及通道数量
int frame_height = frame->height;
int frame_width = frame->width;
int frame_channels = 3;
// 初始化 Mat
Mat img = Mat::zeros(frame_height, frame_width, CV_8UC3);
// 初始化存放 YUV 编码图片的 buffer 内存空间
uchar* yuv_buffer = (uchar*)malloc(frame_height * frame_width * sizeof(uchar)*frame_channels);
// 获取图片原始数据
// Y
for (int i = 0; i < frame_height; i++) {
memcpy(yuv_buffer + frame_width * i,
frame->data[0] + frame->linesize[0] * i,
frame_width);
}
// U
for (int j = 0; j < frame_height / 2; j++) {
memcpy(yuv_buffer + frame_width * frame_height + frame_width / 2 * j,
frame->data[1] + frame->linesize[1] * j,
frame_width / 2);
}
// V
for (int k = 0; k < frame_height / 2; k++) {
memcpy(yuv_buffer + frame_width * frame_height + frame_width / 2 * (frame_height / 2) + frame_width / 2 * k,
frame->data[2] + frame->linesize[2] * k,
frame_width / 2);
}
// 转换为 RGB 编码
YUV420P2RGB32(yuv_buffer, img.data, frame_width, frame_height);
// 释放 buffer 内存空间
free(yuv_buffer);
return img;
}
void YUV420P2RGB32(const uchar *yuv_buffer_in, const uchar *rgb_buffer_out, int width, int height) {
uchar *yuv_buffer = (uchar *)yuv_buffer_in;
uchar *rgb_buffer = (uchar *)rgb_buffer_out;
int channels = 3;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int index_Y = y * width + x;
int index_U = width * height + y / 2 * width / 2 + x / 2;
int index_V = width * height + width * height / 4 + y / 2 * width / 2 + x / 2;
// 取出 YUV
uchar Y = yuv_buffer[index_Y];
uchar U = yuv_buffer[index_U];
uchar V = yuv_buffer[index_V];
// YCbCr420
int R = Y + 1.402 * (V - 128);
int G = Y - 0.34413 * (U - 128) - 0.71414*(V - 128);
int B = Y + 1.772*(U - 128);
// Y'Cb'Cr'420
// int R = 1.164 * (Y - 16) + 1.596 * (V - 128);
// int G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.392 * (U - 128);
// int B = 1.164 * (Y - 16) + 2.017 * (U - 128);
// 确保取值范围在 0 - 255 中
R = (R < 0) ? 0 : R;
G = (G < 0) ? 0 : G;
B = (B < 0) ? 0 : B;
R = (R > 255) ? 255 : R;
G = (G > 255) ? 255 : G;
B = (B > 255) ? 255 : B;
rgb_buffer[(y*width + x)*channels + 2] = uchar(R);
rgb_buffer[(y*width + x)*channels + 1] = uchar(G);
rgb_buffer[(y*width + x)*channels + 0] = uchar(B);
}
}
}
从 AVFrame 中取出帧(YUV)保存为 Mat 格式的更多相关文章
- Java 打开Excel,往Excel中存入值,保存的excel格式分别是xls和xlsx
https://mirrors.cnnic.cn/apache/poi/xmlbeans/release/src/ package Excel; import org.apache.poi.hssf. ...
- Android中使用MediaCodec硬件解码,高效率得到YUV格式帧,快速保存JPEG图片(不使用OpenGL)(附Demo)
MediaCodec的使用demo: https://github.com/vecio/MediaCodecDemo https://github.com/taehwandev/MediaCodecE ...
- 从数组中取出N个元素的所有组合——递归实现
https://www.cnblogs.com/null00/archive/2012/04/27/2473788.html 今天在做POJ 1753时,需要枚举一个数组中所有组合.之前也遇到过类似的 ...
- 深度学习原理与框架-CNN在文本分类的应用 1.tf.nn.embedding_lookup(根据索引数据从数据中取出数据) 2.saver.restore(加载sess参数)
1. tf.nn.embedding_lookup(W, X) W的维度为[len(vocabulary_list), 128], X的维度为[?, 8],组合后的维度为[?, 8, 128] 代码说 ...
- matlab中如何将视频保存成图像
利用MATLAB将视频的每一帧保存成一幅图像,并自动命名.本文方法简单,容易学习. 首先,读入视频.代码如下: mov = VideoReader('xxxxxx.avi'); % 将xxxxxx.a ...
- FFmpeg——AVFrame中 的 data
AVFrame中 的 data 的定义如下: typedef struct AVFrame { #define AV_NUM_DATA_POINTERS 8 /** * pointer to the ...
- yii2通过foreach循环遍历在一个用户组中取出id去另一表里查寻信息并且带着信息合并原数组信息---案例
yii2通过foreach循环遍历在一个用户组中取出id去另一表里查寻信息并且带着信息合并元数组信息---案例 public function actionRandomLists(){ //查询到了所 ...
- c#---部分;把数组或者结构体存入集合里,然后再从集合中取出之后,输出;foreach既可以用到提取数组重点额数据,也可以提取集合中的数据(前提是集合中的元素是相同数据类型)
1.输入班级人数,统计每个人的姓名,性别,年龄:集合与数组 //Console.Write("请输入班级人数:"); //int a = int.Parse(Console.Rea ...
- 在SQL中取出字符串中数字部分或在SQL中取出字符部分
在SQL中取出字符串中数字部分或在SQL中取出字符部分 编写人:CC阿爸 2013-10-18 近来在开发一个项目时,一包含数字的字符串,需要取出中间的数字部分进行排序.经过baidu搜索.并结合自己 ...
随机推荐
- linux命令详解——yum
1.如果不知道确切名字可以:rpm -qa|grep pkgname 2.查看软件安装的文件:rpm -qpl pkgname 3.如果不知道提供某个软件的包是叫什么,可以使用类似下面的写法: yum ...
- redis弱密码漏洞利用
背景: redis无认证,或者弱密码,可以成功连接到redis服务器 反弹shell拿到的权限取决于redis的启动账号 操作: 1. Centos7安装redis客户端 #yum install r ...
- IO模型(epoll)--详解-03
写在前面 epoll是开发linux高性能服务器的必备技术至,epoll本质,是服务端程序员的必须掌握的知识. 七.epoll的原理和流程 本节会以示例和图表来讲解epoll的原理和流程. 创建epo ...
- PAT Basic 1005 继续(3n+1)猜想 (25 分)
卡拉兹(Callatz)猜想已经在1001中给出了描述.在这个题目里,情况稍微有些复杂. 当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数.例如对 n=3 进行验证的时 ...
- log4net 报错
之前在网上学习了一种log4net的日志监控,这种方式我觉得很不错,至少我个人认为很好,但是最紧缺发现一个为题,就是再session过期的时候页面跳转时候 会报错,这个搞了很久没搞明白,我直接用例子讲 ...
- JS 转Boolean的两张方法
// 1.Boolean() console.log(Boolean(123)); // true console.log(Boolean(undefined)); // false console. ...
- BZOJ3555 [Ctsc2014]企鹅QQ[暴力+字符串hash]
菜到自闭,一道省选小水题都能给我做繁. 要求有一位不同,则对每个串每一位暴力枚举把这一位删掉,放一个分隔符,算一下hash,插表,相似的都应该会被插入同一个桶.最后把hash统计一下即可.复杂度$O( ...
- CSS选择器的权重与优先规
我们把特殊性分为4个等级,每个等级代表一类选择器,每个等级的值为其所代表的选择器的个数乘以这一等级的权值,最后把所有等级的值相加得出选择器的特殊值. 4个等级的定义如下: 第一等:代表内联样式,如: ...
- php上传文件代码解析
思想:把html的input标签组织成一个数组,然后去重 关键技术涉及的函数 is_dir mkdir move_uploaded_file() 涉及的数组 预定义数组$_FILES 步骤一:检查上传 ...
- Linux下C++编译(代码高亮自动换行)
1.环境准备 在ubuntu中要想编译c程序可以安装gcc编译器,编译c++的话就不能使用gcc了,要使用g++编译器. 安装gcc或是g++可以在新立得软件包管理器中直接搜索后安装或是使用终端文字命 ...