RGB24和YUV420
RGB24简介
RGB24使用24位来表示一个像素,RGB分量都用8位表示,取值范围为0-255。注意在内存中RGB各分量的排列顺序为:BGR BGR BGR…。通常可以使用RGBTRIPLE数据结构来操作一个像素,它的定义为:
typedef struct tagRGBTRIPLE {
BYTE rgbtBlue; // 蓝色分量
BYTE rgbtGreen; // 绿色分量
BYTE rgbtRed; // 红色分量
} RGBTRIPLE;
YUV420简介
1.YUV定义:
“Y”表示明亮度(Luminance或Luma),也就是灰度值;而“U”和“V” 表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。YUV 的优点之一是,色度频道的采样率可比 Y 频道低,同时不会明显降低视觉质量。
2.YUV 4:2:0采样
色度格式4:2:0 表示 2:1 的水平下采样,2:1 的垂直下采样。4:2:0 采样有两种常见的变化形式。其中一种形式用于 MPEG-2 视频,另一种形式用于 MPEG-1 以及 ITU-T recommendations H.261 和 H.263。图 1 显示了 MPEG-1 方案中使用的采样网格,图 2 显示了 MPEG-2 方案中使用的采样网格。
3.YUV格式通常有两大类:
打包(packed)格式:将YUV分量存放在同一个数组中,通常是几个相邻的像素组 成一个宏像素(macro-pixel);
平面(planar)格式:使用三个数组分开存放YUV三个分量,就像是一个三维平面一样。
而YUV420 是一种planar数据存储, 以720×488大小图象YUV420 planar为例,其存储格式是: 共大小为(720×480×3>>1)字节,分为三个部分: Y分量720×480)个字节,U(Cb)分量(720×480>>2)个字节,V(Cr)分量(720×480>>2)个字节 。三个部分内部均是行优先存储,三个部分之间是Y,U,V 顺序存储。
实验原理
1.YUV420数据的存储格式(见上)
2.RGB转YUV公式
YUV(256 级别) 可以从8位 RGB 直接计算:
Y = 0.299 R + 0.587 G + 0.114 B
U = - 0.1687 R - 0.3313 G + 0.5 B + 128
V = 0.5 R - 0.4187 G - 0.0813 B + 128
反过来,RGB 也可以直接从YUV (256级别) 计算:
R = Y + 1.402 (Cr-128)
G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
B = Y + 1.772 (Cb-128)
在代码中各个公式多+0.5是为了四舍五入的操作
RGB转换成YUV代码实现
#include <stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<math.h>
constexpr auto width = 256;
constexpr auto height = 256;
#define MY(a,b,c) (( a* 0.2989 + b* 0.5866 + c* 0.1145) + 0.5)
#define MU(a,b,c) (( a*(-0.1688) +b*(-0.3312) + c* 0.5000 + 128 + 0.5))
#define MV(a,b,c) (( a* 0.5000 + b*(-0.4184) + c*(-0.0816) + 128 + 0.5))
#define DY(a,b,c) (MY(a,b,c) > 255 ? 255: (MY(a,b,c) < 0 ? 0 : MY(a,b,c)))
#define DU(a,b,c) (MU(a,b,c) > 255 ? 255: (MU(a,b,c) < 0 ? 0 : MU(a,b,c)))
#define DV(a,b,c) (MV(a,b,c) > 255 ? 255: (MV(a,b,c) < 0 ? 0 : MV(a,b,c)))
int main(int argc, char* argv[])
{
FILE* rgbFile = NULL;
FILE* yuvFile = NULL;
//打开rgb图像
errno_t err;
err = fopen_s(&rgbFile, argv[1], "rb");
if (err == 0)
{
printf("打开rgb文件成功!\n");
}
else printf("打开rgb文件失败!\n");
err = fopen_s(&yuvFile, "down2.yuv", "wb");
if (err == 0)
{
printf("打开yuv文件成功!\n");
}
else printf("打开yuv文件失败!\n");
//分配动态内存
unsigned char* RGB = NULL;
unsigned char* yBuff = NULL;
unsigned char* uBuff = NULL;
unsigned char* vBuff= NULL;
unsigned int imgSize = width * height;
RGB = (unsigned char*)malloc(sizeof(unsigned char) * imgSize * 3);
yBuff = (unsigned char*)malloc(sizeof(unsigned char) * imgSize);
uBuff = (unsigned char*)malloc(sizeof(unsigned char) * imgSize / 4);
vBuff = (unsigned char*)malloc(sizeof(unsigned char) * imgSize / 4);
fread(RGB, sizeof(unsigned char), 3 * width * height, rgbFile);
for (int i = 0; i < width * height; i++)
{
yBuff[i] = (unsigned char)(DY(RGB[3 * i + 2], RGB[3 * i + 1], RGB[3 *i]));
}
int k = 0;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if (i % 2 == 0 && j % 2== 0)
{
uBuff[k] = (unsigned char)(DU(RGB[3 * (width * i + j) + 2], RGB[3 * (width * i + j) + 1], RGB[3 *(width * i + j)]));
vBuff[k] = (unsigned char)(DV(RGB[3 * (width * i + j) + 2], RGB[3 * (width * i + j) + 1], RGB[3 *(width * i + j)]));
k++;
}
}
}
fwrite(yBuff, sizeof(unsigned char),imgSize, yuvFile);
fwrite(uBuff, sizeof(unsigned char), imgSize / 4, yuvFile);
fwrite(vBuff, sizeof(unsigned char), imgSize / 4, yuvFile);
if(RGB!=NULL)
free(RGB);
if (yBuff != NULL)
free(yBuff);
if (uBuff != NULL)
free(uBuff);
if (vBuff != NULL)
free(vBuff);
if (rgbFile != NULL)
fclose(rgbFile);
if (yuvFile != NULL)
fclose(yuvFile);
return 0;
}
YUV转换成RGB代码实现
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>
constexpr auto width = 256;
constexpr auto height = 256;
#define MB(a,b,c) (( a + 1.722*(b-128) + 0.5))
#define MG(a,b,c) (( a - 0.34413*(b-128) - 0.71414*(c-128)+0.5))
#define MR(a,b,c) (( a + 1.4075*(c-128)+0.5))
#define DB(a,b,c) (MB(a,b,c) > 255 ? 255: (MB(a,b,c) < 0 ? 0 : MB(a,b,c)))
#define DG(a,b,c) (MG(a,b,c) > 255 ? 255: (MG(a,b,c) < 0 ? 0 : MG(a,b,c)))
#define DR(a,b,c) (MR(a,b,c) > 255 ? 255: (MR(a,b,c) < 0 ? 0 : MR(a,b,c)))
int main(int argc, char* argv[])
{
FILE* yuvFile;
FILE* rgbFile;
unsigned char* YUV;
unsigned char* RGB;
//open the file
errno_t err;
err = fopen_s(&yuvFile, argv[1], "rb");
if (err == 0)
{
printf("打开yuv文件成功!\n");
}
else printf("打开yuv文件失败!\n");
err = fopen_s(&rgbFile, "down.rgb", "wb");
if (err == 0)
{
printf("打开rgb文件成功!\n");
}
else printf("打开rgb文件失败!\n");
//allocate
YUV = (unsigned char*)malloc(sizeof(unsigned char) * width * height * 3 / 2);
RGB = (unsigned char*)malloc(sizeof(unsigned char) * width * height * 3);
//read the yuvfile
fread(YUV, sizeof(unsigned char), width * height * 3 / 2, yuvFile);
//yuv2rgb
int Y, U, V;
int i, j;
int cwidth = width >> 1;
for (i = 0; i < height; ++i)
{
for (j = 0; j < width; ++j)
{
Y = *(YUV + i * width + j);
U = *(YUV + width * height + (i >> 1)* cwidth + (j >> 1));
V = *(YUV + width * height * 5 / 4 + (i >> 1)* cwidth + (j >> 1));
*(RGB + (i * width + j) * 3) = (unsigned char)(DB(Y, U, 0)); //B = Y +1.779*(U-128)
*(RGB + (i * width + j) * 3 + 1) = (unsigned char)(DG(Y, U, V)); //G = Y-0.3455*(U-128)-0.7169*(V-128)
*(RGB + (i * width + j) * 3 + 2) = (unsigned char)(DR(Y, 0, V)); //R = Y+1.4075*(V-128)
}
}
// write the rgbfile
fwrite(RGB, sizeof(unsigned char), width * height * 3 ,rgbFile);
if (YUV != NULL)
free(YUV);
if (RGB != NULL)
free(RGB);
if (rgbFile != NULL)
fclose(rgbFile);
if (yuvFile != NULL)
fclose(yuvFile);
return 0;
}
实验结果及分析
在视觉上误差很小,但实际上由于数据转换时产生的计算误差,量化误差和YUV本身的U,V分量舍弃了3/4像素点的数据,两幅图的误差不可避免。