yuv与rgb图像格式转换
一、前言
本次试验内容为将256*256,采样为4:2:0的yuv图像转为rgb格式。老师提供了rgb2yuv的源码,经过分析,发现源码已经极其优雅高效,命名方式合理,内存分配刚刚好,还运用了查找表的方法,以空间换时间负责度。故此次直接以源码为基础,稍加改动得到其逆变换。
二、公式推导
1.rgb2yuv
在电视系统中,将红绿蓝称为三基色,分别用( R e ) , ( G e ) , ( G b ) 表示。电视显示器上五颜六色的光便是由这三种色彩的荧光粉调制而成的。
亮度方程:Y=0.2990R+0.5870G+0.1140B
由此可得:
Y=0.2990R+0.5870G+0.1140B
U = B − Y = − 0.2990 R − 0.5870 G + 0.8860 B
V = R − Y = 0.7010 R − 0.5870 G − 0.1140 B
2.yuv2rgb
由以上公式做逆变换,可得:
变换整理后最终形式为:
R = (298 Y+411 V - 57376) / 256
G = (298 Y - 101 U - 211 V + 35168) / 256
B = (298 Y + 519 U - 71200) / 256
三、代码实现
1.读取图像
yuvFile = fopen(yuvFileName, "rb");if (yuvFile == NULL){printf("cannot find yuv file\n");exit(1);}else{printf("The input yuv file is %s\n", yuvFileName);}/* open the RAW file */rgbFile = fopen(rgbFileName, "wb");if (rgbFile == NULL){printf("cannot find rgb file\n");exit(1);}else{printf("The output rgb file is %s\n", rgbFileName);}/* get an output buffer for a frame */rgbBuf = (u_int8_t*)malloc(frameWidth * frameHeight * 3);/* get the input buffers for a frame */yBuf = (u_int8_t*)malloc(frameWidth * frameHeight);uBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);vBuf = (u_int8_t*)malloc((frameWidth * frameHeight) / 4);fread(yBuf, 1, frameWidth * frameHeight, yuvFile);
fread(uBuf, 1, frameWidth * frameHeight/4, yuvFile);
fread(vBuf, 1, frameWidth * frameHeight/4, yuvFile)
虽然这次使用的c++语言,但rgb与yuv的读取思路与上次基本一致。通过限制每次读取的尺寸,将文件存储到每个提前分配好空间的buffer。
2.yuv转rgb
注意:在使用YUVviewerPlus(见下图)打开rgb文件时,会将图像认为bmp图像,打开后会是上下翻转的形式。但在本次实验时直接使用上次的自己写的python文件来打开rgb图像,故无需再对图像进行翻转。
if (!flip) {} else {for (int i = 0; i < x_dim; i++){for (int j = 0; j < y_dim; j++){g = b + 1;r = b + 2;int b_tmp, g_tmp, r_tmp;b_tmp = ((YUVRGB298[*y] + YUVRGB519[*u] - 71200) / 256);g_tmp = ((YUVRGB298[*y] + YUVRGBF101[*u] + YUVRGBF211[*v] + 35168) / 256);r_tmp = ((YUVRGB298[*y] + YUVRGB411[*v] - 57376) / 256);if (b_tmp < 0) b_tmp = 0;if (b_tmp > 255) b_tmp = 255;*b = (unsigned char)b_tmp;if (g_tmp < 0) g_tmp = 0;if (g_tmp > 255) g_tmp = 255;*g = (unsigned char)g_tmp;if (r_tmp < 0) r_tmp = 0;if (r_tmp > 255) r_tmp = 255;*r = (unsigned char)r_tmp;b += 3;y++;if (i % 2 == 0 && j == 0) //奇数行开头{u -= y_dim / 2;v -= y_dim / 2;k -= y_dim / 2;}if (j % 2 != 0)//偶数列{u++;v++;k++;}}}}
代码关键解读:
a) 使用查找表
void InitLookupTable()
{int i;for (i = 0; i < 256; i++) YUVRGB298[i] = (float)298 * i;for (i = 0; i < 256; i++) YUVRGB411[i] = (float)411 * i;for (i = 0; i < 256; i++) YUVRGBF101[i] = (float)-101 * i;for (i = 0; i < 256; i++) YUVRGBF211[i] = (float)-211 * i;for (i = 0; i < 256; i++) YUVRGB519[i] = (float)519 * i;
}
在进行运算之前,提前将可能的取值(0-255)全部计算,并将结果保存在数组中。如此在实际运算中直接查找数组提取结果值就可以了,大大提升了代码效率,是一种空间换取时间的做法。
在日后编写代码中也要常记这个tip。
b) 数值溢出
若是直接将运算结果赋值给rbg指针,将会是下图结果。
(在运算上图时有纰漏,误写了算法的公式导致天空色彩有误)
可以看到图像出现了很多饱和度较高,色彩一致的斑点。这是由于在计算后数值可能低于0或高于255,若是直接赋值给unsigned char类型变量会导致数值溢出。故先赋值给int类型变量,做一个溢出判定。
int b_tmp, g_tmp, r_tmp;b_tmp = ((YUVRGB298[*y] + YUVRGB519[*u] - 71200) / 256);g_tmp = ((YUVRGB298[*y] + YUVRGBF101[*u] + YUVRGBF211[*v] + 35168) / 256);r_tmp = ((YUVRGB298[*y] + YUVRGB411[*v] - 57376) / 256);if (b_tmp < 0) b_tmp = 0;if (b_tmp > 255) b_tmp = 255;*b = (unsigned char)b_tmp;if (g_tmp < 0) g_tmp = 0;if (g_tmp > 255) g_tmp = 255;*g = (unsigned char)g_tmp;if (r_tmp < 0) r_tmp = 0;if (r_tmp > 255) r_tmp = 255;*r = (unsigned char)r_tmp;
c) 指针移动
此实验yuv图像为4:2:0格式,采样点如下图:
在rgb2yuv代码中,需要对运算后的u,v的buffer进行下采样,将256256的空间转化为128128的尺寸,代码如下:
for (j = 0; j < y_dim/2; j ++){psu = sub_u_buf + j * x_dim / 2;psv = sub_v_buf + j * x_dim / 2;pu1 = u_buffer + 2 * j * x_dim;pu2 = u_buffer + (2 * j + 1) * x_dim;pv1 = v_buffer + 2 * j * x_dim;pv2 = v_buffer + (2 * j + 1) * x_dim;for (i = 0; i < x_dim/2; i ++){*psu = (*pu1 + *(pu1+1) + *pu2 + *(pu2+1)) / 4;*psv = (*pv1 + *(pv1+1) + *pv2 + *(pv2+1)) / 4;psu ++;psv ++;pu1 += 2;pu2 += 2;pv1 += 2;pv2 += 2;}}
而在yuv2rgb时,需要将128128的u,v buffer上采样为256256。但为了代码的简洁高效,没有这样做。而是直接利用指针的移动达到此目的。
if (i % 2 == 0 && j == 0) //奇数行开头{u -= y_dim / 2;v -= y_dim / 2;k -= y_dim / 2;}if (j % 2 != 0)//偶数列{u++;v++;k++;}
在横向,每当偶数列时u,v向下移动一个;
在纵向,每当到达奇数行开头时,u,v往回移动128个,重新遍历一遍本行数值。
最终结果
原图 | 转换后 |
---|---|
四、误差分析
虽然输出图像肉眼看起来无异,但在计算过程中,有以下几个环节会产生误差:
1.采样。在rgb图像yuv图像过程中,由于4:2:0的采样格式,u,v像素值变为原来的1/4,损失了大量信息。当再由yuv图像转为rgb时,信息量已经不是完整的了。
2.YUV与RGB色彩空间并不重合,在色彩空间变换时存在溢出。
3.在浮点运算时产生误差。
yuv与rgb图像格式转换相关推荐
- 【DSP开发】【VS开发】YUV与RGB格式转换
[视频处理]YUV与RGB格式转换 YUV格式具有亮度信息和色彩信息分离的特点,但大多数图像处理操作都是基于RGB格式. 因此当要对图像进行后期处理显示时,需要把YUV格式转换成RGB格式. RGB与 ...
- yuv与rgb的转换矩阵推导过程
文章目录 yuv与rgb的转换矩阵推导过程 yuv与rgb互转公式 yuv420p转rgb888矩阵 优化一下 参考 yuv与rgb的转换矩阵推导过程 在网上经常看到一些shader里用了矩阵就能把y ...
- 解码失败会显示绿屏,及yuv和rgb简单理解
音视频图片相关基础: 1:rgb 红绿蓝 光的三原色 在RGB中,一幅图像有三个独立的图像平面或通道组成:红,绿,蓝(以及第四个通道透明度).RGB(255,23,140)是光的三原色,也即红绿蓝Re ...
- YUV420P、YUV420SP、NV12、NV21和RGB互相转换并存储为JPEG以及PNG图片
音视频实践学习 android全平台编译ffmpeg以及x264与fdk-aac实践 ubuntu下使用nginx和nginx-rtmp-module配置直播推流服务器 android全平台编译ffm ...
- sws_scale转yuv到rgb
在linux上开发ffmpeg需要安装的dev库 sudo apt-get install libavcodec-dev sudo apt-get install libavformat-dev su ...
- RAW、RGB 、YUV三种图像格式理解
文章目录 1. 背景 2. 相关概念 2.1 颜色与色彩空间 2.2 RAW图像 2.3 RGB图像 2.4 YUV图像 3. 分类简图 RAW.RGB .YUV三种图像格式理解 1. 背景 在工作中 ...
- 【Android FFMPEG 开发】FFMPEG AVFrame 图像格式转换 YUV - RGBA ( 获取 SwsContext | 初始化图像数据存储内存 | 图像格式转换 )
文章目录 I . FFMPEG AVFrame 图像数据帧处理 前置操作 II . FFMPEG 解码 AVPacket 数据到 AVFrame 流程 III. FFMPEG 解码前后的图像格式 IV ...
- ffmpeg 保存图片 将rgb数据_FFMPEG 实现 YUV,RGB各种图像原始数据之间的转换(swscale)...
FFMPEG中的swscale提供了视频原始数据(YUV420,YUV422,YUV444,RGB24...)之间的转换,分辨率变换等操作,使用起来十分方便,在这里记录一下它的用法. swscale主 ...
- android raw rgb 转换器,raw图像格式转换工具——Iridient Developer Mac
原标题:raw图像格式转换工具--Iridient Developer Mac Iridient Developer是一款功能强大的RAW图像转换应用程序,专为Mac OS X设计和优化.Iridie ...
最新文章
- 刚刚学Asp.Net的人要告诉的几件事
- Supervisor 进程管理工具
- proxychains 使用指北
- react gps坐标转换_手持GPS的三参数计算方法
- 禁用cookie后怎么使用session_Session 和 Cookie 的区别?你在项目中哪些地方使用了?...
- c语言程序整数四则运算,c语言中三个整数随机的四则运算
- java char 8192_java.net.ProtocolException:预期229个字节,但收到8192
- JAVA中BigDecimal的字符化输出
- jsp ---- JSTL
- C# ASP.NET MVC:使用Cookie记住账号密码
- vCheck 5.0
- c语言 键盘输入结构体,C语言结构体问题
- Android音视频开发
- 「镁客·请讲」艾拉比芮亚楠:当OTA普及,我们将在物联网和车联网看到三个变化...
- Tableau仪表板制作
- FX5U Socket通信
- 电机控制进阶——PID速度控制
- jquery遍历节点的方法
- 如何在有限的plt下getshellcscctf_2019_qual_babystack
- Ubuntu 装搜过输入法后桌面黑屏
热门文章
- 阿里云HaaS100物联网开发板学习笔记(六)做个智能灯---一个完整的开发例子
- 简单演示程序序列号的破解
- android 处理home键,android处理home键的方法
- [PyQt5]基本控件24 - 时间日期编辑框QDateTimeEdit
- 农妇守护瘫痪丈夫27年 单独抚育女儿撑起家庭
- c#简单几步实现圆角按钮
- 北京市市场主体发展情况报告 (zz)
- 计算机等级考试二级要学PS,计算机二级考试PS要考什么,和考试的方式是什么?...
- 手把手教你使用Typecho搭建自己的个人博客
- 上海市“专精特新”中小企业认定条件及奖励政策解读