彩色空间转换(RGB ⇔ YUV)

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.实现代码

本菜鸡在代码实现过程中,经历了如下几个步骤:

  1. 翻看之前的知识,对程序的实现有一个大致的逻辑
  2. 编写程序
  3. 调试程序,使结果图片尽量与原图相似
  4. 参考他人代码,用查找表优化代码结构,使之简洁易懂

具体实现代码如下:

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.yuvretrans.rgb
在这里插入图片描述在这里插入图片描述

不难发现,湖中有一个红点…经过与大神交流沟通后,此问题依然无解,待有眉目后更新博客。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值