一、前言

本次试验内容为将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图像格式转换相关推荐

  1. 【DSP开发】【VS开发】YUV与RGB格式转换

    [视频处理]YUV与RGB格式转换 YUV格式具有亮度信息和色彩信息分离的特点,但大多数图像处理操作都是基于RGB格式. 因此当要对图像进行后期处理显示时,需要把YUV格式转换成RGB格式. RGB与 ...

  2. yuv与rgb的转换矩阵推导过程

    文章目录 yuv与rgb的转换矩阵推导过程 yuv与rgb互转公式 yuv420p转rgb888矩阵 优化一下 参考 yuv与rgb的转换矩阵推导过程 在网上经常看到一些shader里用了矩阵就能把y ...

  3. 解码失败会显示绿屏,及yuv和rgb简单理解

    音视频图片相关基础: 1:rgb 红绿蓝 光的三原色 在RGB中,一幅图像有三个独立的图像平面或通道组成:红,绿,蓝(以及第四个通道透明度).RGB(255,23,140)是光的三原色,也即红绿蓝Re ...

  4. YUV420P、YUV420SP、NV12、NV21和RGB互相转换并存储为JPEG以及PNG图片

    音视频实践学习 android全平台编译ffmpeg以及x264与fdk-aac实践 ubuntu下使用nginx和nginx-rtmp-module配置直播推流服务器 android全平台编译ffm ...

  5. sws_scale转yuv到rgb

    在linux上开发ffmpeg需要安装的dev库 sudo apt-get install libavcodec-dev sudo apt-get install libavformat-dev su ...

  6. RAW、RGB 、YUV三种图像格式理解

    文章目录 1. 背景 2. 相关概念 2.1 颜色与色彩空间 2.2 RAW图像 2.3 RGB图像 2.4 YUV图像 3. 分类简图 RAW.RGB .YUV三种图像格式理解 1. 背景 在工作中 ...

  7. 【Android FFMPEG 开发】FFMPEG AVFrame 图像格式转换 YUV - RGBA ( 获取 SwsContext | 初始化图像数据存储内存 | 图像格式转换 )

    文章目录 I . FFMPEG AVFrame 图像数据帧处理 前置操作 II . FFMPEG 解码 AVPacket 数据到 AVFrame 流程 III. FFMPEG 解码前后的图像格式 IV ...

  8. ffmpeg 保存图片 将rgb数据_FFMPEG 实现 YUV,RGB各种图像原始数据之间的转换(swscale)...

    FFMPEG中的swscale提供了视频原始数据(YUV420,YUV422,YUV444,RGB24...)之间的转换,分辨率变换等操作,使用起来十分方便,在这里记录一下它的用法. swscale主 ...

  9. android raw rgb 转换器,raw图像格式转换工具——Iridient Developer Mac

    原标题:raw图像格式转换工具--Iridient Developer Mac Iridient Developer是一款功能强大的RAW图像转换应用程序,专为Mac OS X设计和优化.Iridie ...

最新文章

  1. 刚刚学Asp.Net的人要告诉的几件事
  2. Supervisor 进程管理工具
  3. proxychains 使用指北
  4. react gps坐标转换_手持GPS的三参数计算方法
  5. 禁用cookie后怎么使用session_Session 和 Cookie 的区别?你在项目中哪些地方使用了?...
  6. c语言程序整数四则运算,c语言中三个整数随机的四则运算
  7. java char 8192_java.net.ProtocolException:预期229个字节,但收到8192
  8. JAVA中BigDecimal的字符化输出
  9. jsp ---- JSTL
  10. C# ASP.NET MVC:使用Cookie记住账号密码
  11. vCheck 5.0
  12. c语言 键盘输入结构体,C语言结构体问题
  13. Android音视频开发
  14. 「镁客·请讲」艾拉比芮亚楠:当OTA普及,我们将在物联网和车联网看到三个变化...
  15. Tableau仪表板制作
  16. FX5U Socket通信
  17. 电机控制进阶——PID速度控制
  18. jquery遍历节点的方法
  19. 如何在有限的plt下getshellcscctf_2019_qual_babystack
  20. Ubuntu 装搜过输入法后桌面黑屏

热门文章

  1. 阿里云HaaS100物联网开发板学习笔记(六)做个智能灯---一个完整的开发例子
  2. 简单演示程序序列号的破解
  3. android 处理home键,android处理home键的方法
  4. [PyQt5]基本控件24 - 时间日期编辑框QDateTimeEdit
  5. 农妇守护瘫痪丈夫27年 单独抚育女儿撑起家庭
  6. c#简单几步实现圆角按钮
  7. 北京市市场主体发展情况报告 (zz)
  8. 计算机等级考试二级要学PS,计算机二级考试PS要考什么,和考试的方式是什么?...
  9. 手把手教你使用Typecho搭建自己的个人博客
  10. 上海市“专精特新”中小企业认定条件及奖励政策解读