RGB格式图片转YUV图片(超级菜鸟版)

根据遇到的问题分析实验原理

由于人眼对亮度更加敏感,对色度信号的差异不太敏感,在彩色图片的传输过程中不使用RGB信号分量而利用亮度方程以YUV信号分量传输,Y为亮度信号,U,V为色差信号,可以通过降低色差信号的数据量实现压缩。RGB与YUV对应关系:
{Y=0.299R+0.587G+0.114B+16U=−0.1684R−0.3316G+0.5B=0.564(B−Y)+128(1)V=0.5R−0.4187G−0.0813B=0.713(R−Y)+128\begin{cases} Y=0.299R+0.587G+0.114B+16\\ U=-0.1684R-0.3316G+0.5B=0.564(B-Y)+128\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\, (1)\\ V=0.5R-0.4187G-0.0813B=0.713(R-Y)+128 \end{cases} ⎩⎪⎨⎪⎧​Y=0.299R+0.587G+0.114B+16U=−0.1684R−0.3316G+0.5B=0.564(B−Y)+128(1)V=0.5R−0.4187G−0.0813B=0.713(R−Y)+128​

其中,为了使色差信号的动态范围控制在[-0.5, 0.5],需要进行量化前的归一化处理,需要引入数字色差信号的压缩系数(分别为0.564与0.713)。

在数字电视的传输中,在进行8 bit量化时,需要在上下两端留出一定的余量,作为信号超越动态范围的保护带。具体地:

对于亮度信号,在256级的上端留出20级,下端留出16级作为余量,即Y的动态范围为16-235;
对于两个色差信号,在256级的上端留出15级,下端留出16级作为余量,即U、V的动态范围为16-240。

所以会有一定的对上式会有压缩处理
{Y′=int235−16255×Y+16U′=int240−16255×Y×0.713+128(2)V′=int240−16255×V×0.564+128\begin{cases} Y'=int{\frac{235-16}{255}\times Y+16}\\ U'=int{\frac{240-16}{255}\times Y\times 0.713+128}\,\,\,\,\,\,\,\,\,\,\,\,\,\,(2)\\ V'=int{\frac{240-16}{255}\times V\times 0.564+128} \end{cases} ⎩⎪⎨⎪⎧​Y′=int255235−16​×Y+16U′=int255240−16​×Y×0.713+128(2)V′=int255240−16​×V×0.564+128​
由于(2)式主要用于数字电视传输中,对于单幅图片(2)式和(1)式影响不大。但需要注意提升电平!!(不知道为什么都有光斑现象)

原图片

(1)式转换结果

(2)式转换结果


经过博主的一番研究,发现是采样格式出现错误

请忽视丑陋的涂鸦,重点看红色箭头和蓝色箭头,红色箭头是后续改进后的采样,蓝色箭头是原先出现光斑的采样。

(1)式结果

(2)式结果

还是会有偏暗的问题,不过为了方便,真的懒得再改了,就采用(2)式,而且对比原图还是(2)还原效果最接近。但最后还是用了(1).

YUV转RGB公式计算

如果真的认真算的话怕是错误多多,在这里可以用matlab计算逆矩阵。

%format long %多看几位小数
a = [0.299 0.587 0.114;-0.1684 -0.3316 0.5;0.5 -0.4187 -0.0813];
inv(a) % 得到逆矩阵,也就是YUV转RGB的矩阵

结果为:
ans =

1.0000 -0.0000 1.4020
1.0000 -0.3441 -0.7139
1.0000 1.7718 -0.0013
也就是:
R = Y+1.4020V
G = Y-0.3441U-0.7139V
B = Y+1.7718U-0.0013V
但注意!需要减去提升电平!

但是博主做着做着发现,改正后结果居然是这样的!

yuv还原rgb1

还记得我刚刚分析了使用数字压缩后更暗,所以可能我还原时不能对应数字压缩的yuv文件,得用(1)式的文件,so继续改回原来的(1)式

更斑驳了

为什么呢?不应该啊?经过我的一番比对

原来是我没有对rgb数值进行修正!

你看看你看看,Y如果不减去16的话,B还是会有小于16的数值,如果Y-16的话B将出现负值!(有点绕但这都是我一点点改出来的呜呜呜)

所以我们要对rgb倒算出来超出255和小于0的数值进行修正,修正后:

此刻的它是多么漂亮啊啊啊啊

实验代码

这次实验中我才用较为规范的工程写法,所以还是用了较长的时间,而且部分失误报错还没有完善。

头文件(记得加上#include的文件)

一直出现报错FILE未定义,发现头文件中我没有#include<stdio.h>,而FILE类在其中,所以需要加上

#pragma once
#include<iostream>
#include<stdio.h>
#include<math.h>
void initLookupTable();
void _rgb2yuv(FILE* yuvfile, int rgbSize, unsigned char* rgbBuf, unsigned char* yBuf, unsigned char* uBuf, unsigned char* vBuf);
void _yuv2rgb(FILE* rgbfile, int rgbSize, unsigned char* rgbBuf, unsigned char* yBuf, unsigned char* uBuf, unsigned char* vBuf);
void error_data(FILE* rgbFileP, FILE* rgbRecFileP, unsigned char* rgbOriBuffer1, unsigned char* rgbRecBuffer1, int rgbFileSize);

rgb2yuv.cpp文件(存储大量需要用到的函数)(有如上述错误,自行比对修改,防白嫖(其实就是懒得再改了))

#include"RGB2YUV.h"
#include<iostream>
#include<stdio.h>
#include<math.h>float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256], RGBYUV01684[256], RGBYUV03316[256], RGBYUV04187[256], RGBYUV00813[256],RGBYUV05[256];
//建立查找表,避免大量计算(可省略)
void initLookupTable()
{for (int i = 0; i < 256; i++){RGBYUV02990[i] = (float)0.2990 * i;RGBYUV05870[i] = (float)0.5870 * i;RGBYUV01140[i] = (float)0.1140 * i;RGBYUV01684[i] = (float)0.1684 * i;RGBYUV03316[i] = (float)0.3316 * i;RGBYUV04187[i] = (float)0.4187 * i;RGBYUV00813[i] = (float)0.0813 * i;RGBYUV05[i]= (float)0.5 * i;}
}
//rgb转换为yuv
void _rgb2yuv(FILE* yuvfile, int rgbSize, unsigned char* rgbBuf, unsigned char* yBuf, unsigned char* uBuf, unsigned char* vBuf) {//首先计算444采样格式的yuvfloat* uBuf444 = NULL;//下采样前的缓冲区float* vBuf444 = NULL;uBuf444 = new float[rgbSize / 3];vBuf444 = new float[rgbSize / 3];for (int i = 0; i < rgbSize/3; i++) {int b = int(*(rgbBuf+3*i));int g = int(*(rgbBuf+3 * i+1));int r= (int)rgbBuf[3 * i + 2];//根据公式计算存储//数字电视压缩处理测试float y = (219.0/255.0*(RGBYUV02990[r] + RGBYUV05870[g] + RGBYUV01140[b])) + 16.0;yBuf[i] = (unsigned char) y;uBuf444[i] = 0.713*224.0/255.0*(-RGBYUV01684[r] - RGBYUV03316[g] + RGBYUV05[b]) +128.0;vBuf444[i] = 0.564*224.0/255.0*(RGBYUV05[r] - RGBYUV04187[g] - RGBYUV00813[b])+128.0;//float y = ((RGBYUV02990[r] + RGBYUV05870[g] + RGBYUV01140[b])) + 16.0;//yBuf[i] = (unsigned char)y;//uBuf444[i] =  (-RGBYUV01684[r] - RGBYUV03316[g] + RGBYUV05[b]) + 128.0;//vBuf444[i] =  (RGBYUV05[r] - RGBYUV04187[g] - RGBYUV00813[b]) + 128.0;}//原来的444转420采样格式代码,错误!//for (int i = 0; i < rgbSize / 12; i++) {//   uBuf[i] = (unsigned char)uBuf444[i * 4];// vBuf[i]= (unsigned char)vBuf444[i * 4];//}///采样格式更改测试,正确!// 4:4:4 to 4:2:0int w = 256; int h = 256;for (int i = 0; i < h; i += 2){for (int j = 0; j < w; j += 2){uBuf[i / 2 * w / 2 + j / 2] = (unsigned char)uBuf444[i * w + j];vBuf[i / 2 * w / 2 + j / 2] = (unsigned char)vBuf444[i * w + j];}}//写入fwrite(yBuf, sizeof(unsigned char), rgbSize / 3, yuvfile);fwrite(uBuf, sizeof(unsigned char), rgbSize / 12, yuvfile);fwrite(vBuf, sizeof(unsigned char), rgbSize / 12, yuvfile);//释放空间delete[] uBuf444; delete[] vBuf444;}//yuv转rgb
void _yuv2rgb(FILE* rgbfile, int rgbSize, unsigned char* rgbBuf, unsigned char* yBuf, unsigned char* uBuf, unsigned char* vBuf) {int index = 0;int yi = 0;for (int i = 0; i < rgbSize / 12; i++) {//4:1:1for (int bili = 0;bili < 4; bili++) {rgbBuf[index] = unsigned char(yBuf[yi] + 1.7718 * (uBuf[i] - 128));//bindex++; rgbBuf[index] = unsigned char(yBuf[yi] - 0.3441 * (uBuf[i] - 128) - 0.7139 * (vBuf[i] - 128));//gindex++; rgbBuf[index] = unsigned char((yBuf[yi] + 1.4020 * (vBuf[i] - 128)));//ryi++; index++;}}fwrite(rgbBuf, sizeof(unsigned char), rgbSize, rgbfile);}void error_data(FILE* rgbFileP, FILE* rgbRecFileP,unsigned char* rgbOriBuffer1,unsigned char* rgbRecBuffer1,int rgbFileSize) {/本想使用直接文件指针传入再计算差值,但因为主函数已经规定了读写格式和文件指针已经移动,也不愿意重新读取文件,直接利用已经计算好的缓冲区数据算差值,可以再优化!!//rewind(rgbFileP);//fseek(rgbFileP, 0L, SEEK_END);//int rgbFileSize = ftell(rgbFileP);//rewind(rgbFileP);//int yuvFileSize = rgbFileSize / 2;//rewind(rgbRecFileP);FILE* errorFile;fopen_s(&errorFile, "error_data.csv", "wb");//unsigned char* rgbOriBuffer = NULL;//unsigned char* rgbRecBuffer = NULL;//rgbOriBuffer = (unsigned char*)malloc(rgbFileSize);//rgbRecBuffer = (unsigned char*)malloc(rgbFileSize);unsigned char* error_data = (unsigned char*)malloc(rgbFileSize);//fread(rgbOriBuffer, sizeof(unsigned char), rgbFileSize, rgbFileP);//fread(rgbRecBuffer, sizeof(unsigned char), rgbFileSize, rgbRecFileP);//for (int i = 0; i < rgbFileSize; i++) {error_data[i] = abs(rgbOriBuffer1[i] - rgbRecBuffer1[i]);}for (int i = 0; i < rgbFileSize / 3; i++) {int index = 0;fprintf(errorFile, "(%d,%d,%d)  ", int(error_data[3 * i]), int(error_data[3 * i + 1]), int(error_data[3 * i + 2]));index++;if (index == 256) {fprintf(errorFile, "\n");}}//delete[] rgbOriBuffer;//delete[] rgbRecBuffer;//注意释放空间delete[] error_data;fclose(errorFile);
}

主函数main.cpp

#include"RGB2YUV.h"
#include <iostream>
#include <stdio.h>
using namespace std;int main(int argc, char* argv[]) {FILE* rgbOriFileP = NULL;//原RGB图像的文件指针FILE* yuvFileP = NULL;//YUV图像的文件指针FILE* rgbRecFileP = NULL;//复原的RGB的文件指针const char* rgbOriFileName = argv[1];const char* yuvFileName = argv[2];const char* rgbRecFileName = argv[3];int width = 256;int height = 256;unsigned char* rgbOriBuffer = NULL;unsigned char* yBuffer = NULL;unsigned char* uBuffer = NULL;unsigned char* vBuffer = NULL;unsigned char* rgbRecBuffer = NULL;//openif (fopen_s(&rgbOriFileP, rgbOriFileName, "rb") == 0) {cout << "Successfully opened" << rgbOriFileName << endl;}else {cout << "Failed to open" << rgbOriFileName << endl;exit(0);}if (fopen_s(&yuvFileP, yuvFileName, "wb") == 0) {cout << "Successfully opened" << yuvFileName << endl;}else {cout << "Failed to open" << yuvFileName << endl;exit(0);}if (fopen_s(&rgbRecFileP, rgbRecFileName, "wb") == 0) {cout << "Successfully opened" << rgbRecFileName << endl;}else {cout << "Failed to open" << rgbRecFileName << endl;exit(0);}//计算原RGB图像的总字节数fseek(rgbOriFileP, 0L, SEEK_END);int rgbFileSize = ftell(rgbOriFileP);rewind(rgbOriFileP);cout << "the rgb file size:" << rgbFileSize << endl;int yuvFileSize = rgbFileSize / 2;//建立缓冲区,4:2:0yuvrgbOriBuffer = (unsigned char*)malloc(rgbFileSize);rgbRecBuffer= (unsigned char*)malloc(rgbFileSize);yBuffer = (unsigned char*)malloc(rgbFileSize/3);uBuffer = (unsigned char*)malloc(rgbFileSize/12);vBuffer = (unsigned char*)malloc(rgbFileSize/12);fread(rgbOriBuffer, sizeof(unsigned char), rgbFileSize, rgbOriFileP);//RGB图像读入缓冲区initLookupTable();_rgb2yuv(yuvFileP, rgbFileSize,rgbOriBuffer, yBuffer, uBuffer, vBuffer);_yuv2rgb(rgbRecFileP, rgbFileSize, rgbRecBuffer, yBuffer, uBuffer, vBuffer);error_data(rgbOriFileP, rgbRecFileP, rgbOriBuffer, rgbRecBuffer,rgbFileSize);delete[] rgbOriBuffer;delete[] yBuffer;delete[] uBuffer;delete[] vBuffer;delete[] rgbRecBuffer;fclose(rgbOriFileP);fclose(yuvFileP);fclose(rgbRecFileP);}

这个应该没错吧,就是简单的打开文件,调用。

还有读取rgb格式文件,在我求RGB三通道分量熵中有代码,但好像C++确实没方法读取,如果有人搜到了可以评论一下
读取rgb文件代码

实验结果

来秀一波我的成果吧,虽然其实是简单的实验但因为太久没写C++和对YUV,RGB不太熟,所以写了好几个小时。。。。

rgb2yuv无压缩版(1):

rgb2yuv然后再yuv2rgb后的

error_data:

遇见问题

1、头文件 函数声明时用了FILE指针,存储在stdio.h定义,所以需要#include<stdio.h>

2、采样格式失误,导致光斑!

3、反推yuv2rgb公式时手算十分困难!

4、提升电平的加减!

5、需要修正rgb的数值。

生活不易,如果看到这的老师同学师弟师妹(应该不会有师哥姐看到吧哈哈哈)请点个赞哈哈哈哈

RGB格式图片转YUV图片相关推荐

  1. 图片YUV格式与RGB格式的转换

    YUV格式与RGB格式的转换 YUV格式介绍 YUV420.YUV422.YUV444 (1) YUV4:2:0 (2) YUV4:2:2 (3) YUV4:4:4 内存排列方式 YUV与RGB转换 ...

  2. 使用C++实现多张BMP图片转换为YUV动画----附加淡入淡出转场(逐渐变明变暗),及垂直滑像转场(逐行渐变)

    使用C++实现多张BMP图片转换为YUV动画----附加淡入淡出转场(逐渐变明变暗),及垂直滑像转场(逐行渐变) 一.BMP图像简介 1.BMP图像是什么? 2.BMP图像文件结构 1)图象文件头 2 ...

  3. 使用C++实现YUV格式图像与RGB格式图像之间相互转换

    使用C++实现YUV格式图像与RGB格式图像之间相互转换 一.RGB与YUV转换公式 1.RGB转YUV 1)RGB转换亮度与色差信号公试: 2)归一化为YUV的转化公试为: 2.YUV转RGB 二. ...

  4. YUY2(YUV) 与 RGB 格式图片的相互转换 以及 基于YUY2(YUV)的blending

    这是一个项目里使用的,API里从pool里取出的格式都是YUY2,但是图像处理的API库中要求都是jepg格式. YUY2经常用于电视制式以及许多摄像头的输出格式.而我们在处理时经常需要将其转化为RG ...

  5. YUY2(YUV) 与 RGB 格式图片的相互转换

    [版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/jtujtujtu/article/d ...

  6. JPEG/Exif/TIFF格式解读(1):JEPG图片压缩与存储原理分析

    JPEG文件简介 JPEG的全称是JointPhotographicExpertsGroup(联合图像专家小组),它是一种常用的图像存储格式, jpg/jpeg是24位的图像文件格式,也是一种高效率的 ...

  7. 音视频之渲染yuv图片

    音视频之opengl绘制三角形 音视频之opengl渲染图片 音视频之渲染yuv图片 前一篇我们讨论了如何渲染一个普通图片(rgb) 现在我们来讨论如何渲染一个yuv图片. 什么是yuv我们这里有一个 ...

  8. YUV与RGB格式详解

    YUV 是一种颜色编码方法,和它等同的还有 RGB 颜色编码方法. RGB 颜色编码 RGB 三个字母分别代表了 红(Red).绿(Green).蓝(Blue),这三种颜色称为 三原色,将它们以不同的 ...

  9. 分析比较图像RGB格式和YUV格式的存储概率分布

    分析比较图像RGB格式和YUV格式的存储概率分布 实验思路 两个图片文件down.yuv和down.rgb均为二进制存储的图片文件,像素为256* 256.yuv为4:2:0格式,即1份y对应0.25 ...

  10. YUV RGB格式分析,以及对应码率(带宽)计算

    如果排查的思路对你有帮助,请记住 消雨匆匆 . 码字和排查很累,仅此而已 ,硬件不是抄电路,拿烙铁,懂点软件,细致分析,找问题和破案搜集线索是一样的. 忽然前些天和之前的同事聚会,同事聊了一下他面试遇 ...

最新文章

  1. linux 系统装中文输入法 fcitx
  2. python白名单验证-JWT黑名单和白名单
  3. 【转载】关于Java堆和栈的解释,收藏下来以后学习
  4. c++ 结构体初始化_【干货】c语言基础语法——结构体
  5. var lib mysql ib_MYSQL问题解决
  6. 判断五个分数等级划分_压力表精度等级怎么算?压力表精度等级划分及检验项目...
  7. sdut 1465 公共因子
  8. 眼图在通信系统中有什么意义_KT124煤矿调度通信系统和传统调度系统相比有什么优势...
  9. Java学习资料(四)——尚学堂马士兵视频
  10. 罗永浩发声:我的努力很可能失败 但好产品一定要赢
  11. STM32学习过程一
  12. 【cocos2d-x入门实战】微信飞机大战之八:自定义敌机精灵
  13. 牛客算法周周练11A - 切题之路(阅读理解)
  14. CSS中的十二种结构伪类选择器
  15. html如何实现日期下拉菜单,实现一个日期下拉菜单
  16. 江西理工大学计算机考研资料汇总
  17. java程序编译成exe文件_将java程序编译成独立运行的exe文件
  18. 使用CyclicBarrier模拟百米赛跑
  19. css特效2:流光边框
  20. ccp调试常见错误之不应答

热门文章

  1. 海康线阵相机调试指导
  2. 土壤质地标准转换程序Java MVC模式
  3. 数学知识整理:布朗运动与伊藤引理 (Ito‘s lemma)
  4. B区考研学校排名计算机,b区(b区考研学校排名)
  5. 腾讯云带宽收费标准价格表
  6. 第五章:腾讯云有哪些产品
  7. android上下居中,android Spinner:在spinner中垂直居中文本
  8. markdown下载
  9. CentOS之命令方式安装向日葵与内网穿透
  10. st语言和c语言一样,什么是ST语言,一文带你了解ST语言