1.彩色空间转换的基本思想及转换公式
(1)YUV与RGB空间的相互转换
由电视原理可知,亮度和色差信号的构成如下:
- Y=0.2990R+0.5870G+0.1140B
- R-Y=0.7010R-0.5870G-0.1140B
- B-Y=-0.2990R-0.5870G+0.8860B
为了使色差信号的动态范围控制在0.5之间,需要进行归一化,对色差信号引入压缩系数。归一化后的色差信号为:
- U=-0.1684R-0.3316G+0.5B
- V=0.5R-0.4187G-0.0813B
由RGB转YUV的公式可推出YUV转RGB的公式如下,
- R = Y + 1.4075(V - 128)
- G = Y - 0.3455(U - 128) - 0.7169(V - 128)
- B = Y + 1.779(U - 128)
(2) 码电平分配及数字表达式
亮电平信号量化后码电平分配在对分量信号进行8比特均匀量化时,共分为256个等间隔的量化级。为了防止信号变动造成过载,在256级上端留20级,下端留16级作为信号超越动态范围的保护带。
色差信号量化后码电平分配色差信号经过归一化处理后,动态范围为-0.5-0.5,让色差零电平对应码电平128,色差信号总共占225个量化级。在256级上端留15级,下端留16级作为信号超越动态范围的保护带。故在YUV转RGB时,UV分量需要先减去128。
(3)色度格式
4:2:0格式是指色差信号U,V的取样频率为亮度信号取样频率的四分之一,在水平方向和垂直方向上的取样点数均为Y的一半,故UV分量分别所占的空间为Y的1/4。
(4)存储的排列方式
在4:2:0 的色度格式下,
RGB文件的排列顺序为
b1g1r1, b2g2r2,b3g3r3,b4g4r4,…
YUV文件的排列顺序为
Y1,Y2,Y3,Y4,Y5,Y6,Y7,Y8,…
U1,U2,U3,U4,…
V1,V2,V3,V4,…
2.实现代码
本菜鸡在代码实现过程中,经历了如下几个步骤:
- 翻看之前的知识,对程序的实现有一个大致的逻辑
- 编写程序
- 调试程序,使结果图片尽量与原图相似
- 参考他人代码,用查找表优化代码结构,使之简洁易懂
具体实现代码如下:
header.h :
#pragma once
void rgb2yuv(unsigned char* RGB, unsigned char* Y,
unsigned char* U, unsigned char* V, int Width, int Height);
void yuv2rgb(unsigned char* R, unsigned char* G,
unsigned char* B, unsigned char* Y, unsigned char* U, unsigned char* V,
int Width, int Height);
rgb2yuv.cpp :
#include<stdio.h>
#include "header.h"
float RGB2YUV02990[256], RGB2YUV05870[256], RGB2YUV01140[256], RGB2YUV01684[256],
RGB2YUV03316[256], RGB2YUV05000[256], RGB2YUV04187[256], RGB2YUV00813[256];
void Initrgb_table() {
for (int i = 0; i < 256; i++)
{
RGB2YUV02990[i] = (float)0.299 * i;
RGB2YUV05870[i] = (float)0.587 * i;
RGB2YUV01140[i] = (float)0.114 * i;
RGB2YUV01684[i] = (float)0.1684 * i;
RGB2YUV03316[i] = (float)0.3316 * i;
RGB2YUV05000[i] = (float)0.5 * i;
RGB2YUV04187[i] = (float)0.4187 * i;
RGB2YUV00813[i] = (float)0.0813 * i;
}
}
void rgb2yuv(unsigned char* RGB, unsigned char* Y, unsigned char* U, unsigned char* V, int Width, int Height)
{
int y = 0;
for (int i = 0; i < Width * Height * 3; i = i + 3)
{
Initrgb_table();
Y[y] = RGB2YUV01140[RGB[i]] + RGB2YUV05870[RGB[i + 1]] + RGB2YUV02990[RGB[i + 2]];
//圈定Y范围:16-235
if (Y[y] > 235)
Y[y] = 235;
if (Y[y] < 16)
Y[y] = 16;
y++;
}
int m = 0, n = 0;
for (int i = 0; i < Width * Height * 3;)
{
unsigned char* rgb;
rgb = new unsigned char[Width * Height * 3];
rgb[i] = (RGB[i] + RGB[i + 3] + RGB[i + Width * 3] + RGB[i + Width * 3 + 3])/4;
Initrgb_table();
U[m] = RGB2YUV05000[rgb[i]] - RGB2YUV03316[rgb[i + 1]] - RGB2YUV01684[rgb[i + 2]] + 128;
V[n] = -RGB2YUV00813[rgb[i]] - RGB2YUV04187[rgb[i + 1]] * +RGB2YUV05000[rgb[i + 2]] + 128;
m++;
n++;
//圈定UV范围:16-240
if (U[m] > 240)
U[m] = 240;
if (U[m] < 16)
U[m] = 16;
if (V[n] > 240)
V[n] = 240;
if (V[n] < 16)
V[n] = 16;
if ((i / 3) % 256 == 254)
i = i + 258 * 3;
else
i = i + 6;
delete[]rgb;
}
}
yuv2rgb.cpp :
#include <iostream>
#include "header.h"
float YUV2RGB14075[256], YUV2RGB03455[256], YUV2RGB07169[256], YUV2RGB1779[256];
void Inityuv_table() {
for (int i = 0; i < 256; i++)
{
YUV2RGB14075[i] = (float)1.4075 * (i - 128);
YUV2RGB03455[i] = (float)0.3455 * (i - 128);
YUV2RGB07169[i] = (float)0.7169 * (i - 128);
YUV2RGB1779[i] = (float)1.779 * (i - 128);
}
}
void yuv2rgb(unsigned char* R, unsigned char* G, unsigned char* B, unsigned char* Y, unsigned char* U, unsigned char* V, int Width, int Height)
{
int j = 0;
for (int i = 0; i < Width * Height;)
{
Inityuv_table();
R[i] = Y[i] + YUV2RGB14075[V[j]];
R[i + 1] = Y[i + 1] + YUV2RGB14075[V[j]];
R[i + Width] = Y[i + Width] + YUV2RGB14075[V[j]];
R[i + Width + 1] = Y[i + Width + 1] + YUV2RGB14075[V[j]];
G[i] = Y[i] - YUV2RGB03455[U[j]] - YUV2RGB07169[V[j]];
G[i + 1] = Y[i + 1] - YUV2RGB03455[U[j]] - YUV2RGB07169[V[j]];
G[i + Width] = Y[i + Width] - YUV2RGB03455[U[j]] - YUV2RGB07169[V[j]];
G[i + Width + 1] = Y[i + Width + 1] - YUV2RGB03455[U[j]] - YUV2RGB07169[V[j]];
B[i] = Y[i] + YUV2RGB1779[U[j]];
B[i + 1] = Y[i + 1] + YUV2RGB1779[U[j]];
B[i + Width] = Y[i + Width] + YUV2RGB1779[U[j]];
B[i + Width + 1] = Y[i + Width + 1] + YUV2RGB1779[U[j]];
//防止RGB数据溢出
if (R[i] < 0)
R[i] = 0;
if (R[i] > 255)
R[i] = 255;
if (G[i] < 0)
G[i] = 0;
if (G[i] > 255)
G[i] = 255;
if (B[i] < 0)
B[i] = 0;
if (B[i] > 255)
B[i] = 255;
if (i % 256 == 254)
i = i + 256 + 2;
else
i = i + 2;
j++;
}
}
main.cpp :
#include<stdio.h>
#include <iostream>
#include <cstdio>
#include <fstream>
#include "header.h"
using namespace std;
int main(int argc, char** argv)
{
int Width = 256, Height = 256;
unsigned char* RGB;
unsigned char* Y, * U, * V;
unsigned char* R, * G, * B;
RGB = (unsigned char*)malloc(sizeof(char) * (Width * Height * 3));
R = (unsigned char*)malloc(sizeof(unsigned char) * (Width * Height));
G = (unsigned char*)malloc(sizeof(unsigned char) * (Width * Height));
B = (unsigned char*)malloc(sizeof(unsigned char) * (Width * Height));
Y = (unsigned char*)malloc(sizeof(char) * (Width * Height));
U = (unsigned char*)malloc(sizeof(char) * (Width * Height / 4));
V = (unsigned char*)malloc(sizeof(char) * (Width * Height / 4));
char* rgbFileName = NULL;
char* yuvFileName = NULL;
char* rergbFileName = NULL;
FILE* rgbfile = NULL;
FILE* yuvfile = NULL;
FILE* rergbfile = NULL;
rgbFileName = argv[1];
yuvFileName = argv[2];
rergbFileName = argv[3];
//打开文件
if (fopen_s(&rgbfile, rgbFileName, "rb") == 0)
{
cout << "File-1 opened! " << rgbFileName << "." << endl;
}
else
{
cout << "File-1 not opened! " << rgbFileName << "." << endl;
exit(0);
}
if (fopen_s(&yuvfile, yuvFileName, "wb+") == 0)
{
cout << "File-2 opened! " << yuvFileName << "." << endl;
}
else
{
cout << "File-2 not opened! " << yuvFileName << "." << endl;
exit(0);
}
if (fopen_s(&rergbfile, rergbFileName, "wb") == 0)
{
cout << "File-3 opened!" << rergbFileName << "." << endl;
}
else
{
cout << "File-3 not opened! " << rergbFileName << "." << endl;
exit(0);
}
//读入RGB文件
fread(RGB, sizeof(unsigned char), Width * Height * 3, rgbfile);
fclose(rgbfile);
rgb2yuv(RGB, Y, U, V, Width, Height);
free(RGB);
fwrite(Y, sizeof(unsigned char), Width * Height, yuvfile);
fwrite(U, sizeof(unsigned char), Width * Height / 4, yuvfile);
fwrite(V, sizeof(unsigned char), Width * Height / 4, yuvfile);
fclose(yuvfile);
yuv2rgb(R, G, B, Y, U, V, Width, Height);
for (int i = 0; i < Width * Height; i++)
{
fwrite(&B[i], sizeof(unsigned char), 1, rergbfile);
fwrite(&G[i], sizeof(unsigned char), 1, rergbfile);
fwrite(&R[i], sizeof(unsigned char), 1, rergbfile);
}
free(B);
free(G);
free(R);
fclose(rergbfile);
return 0;
}
原图:
trans.yuv | retrans.rgb |
---|---|
不难发现,湖中有一个红点…经过与大神交流沟通后,此问题依然无解,待有眉目后更新博客。