对于R8G8B8A8格式的颜色,可以通过下面公式转换为YUV(I420)格式。
unsigned char Y = static_cast
unsigned char U = static_cast
unsigned char V = static_cast
如果是浮点型(r,g,b,a @[0, 1])使用类似的方法,具体参考下面网站。
Wiki: http://zh.wikipedia.org/wiki/YUV
YUV(I420): http://www.fourcc.org/yuv.php#IYUV
同时可以使用GPU实现此算法,以下为DX10的一段shader实现GPU的RGBA8到YUV的转化。
准备工作:使用贴图为RGB颜色空间普通颜色贴图格式,三个render target, Y(rgba8,存放四个Y值), U(r8,存放一个U值), V(r8,存放一个V值)。render target的尺寸为原来贴图的width/2, height/2。
// PSENTRY: PS
// VSENTRY: VS
cbuffer constantsVS : register(b0)
{
float4 gv_renderColor;
float4 gv_rectangle; // xy=source texture size, zw=destination size
};
const static float2 quadCorners[4] = { float2(0, 0), float2(1, 0), float2(0, 1), float2(1, 1) };
Texture2D mapColor : register(t0);
sampler mapColorSampler : register(s0);
struct VS_OUTPUT
{
float4 pos : SV_Position;
float2 tex : TexCoord;
};
VS_OUTPUT VS(uint vID : SV_VertexID)
{
VS_OUTPUT outStruct;
outStruct.pos = float4(float2(-1,-1) + quadCorners[vID] * 2, 0, 1);
outStruct.pos.y = -outStruct.pos.y; // NCD y axis is reversed in DirectX
outStruct.tex = quadCorners[vID];
return outStruct;
}
struct PS_OUTPUT
{
float4 y4 : SV_Target0;
float u : SV_Target1;
float v : SV_Target2;
};
float CalcY( uint pid, uint w, uint h )
{
float2 uv = float2( (float)(pid%(w))/w, (float)(pid/(w))/h );
float3 rgb = mapColor.SampleLevel( mapColorSampler, uv, 0).rgb;
return ((65.738*rgb.x + 129.057*rgb.y + 25.064*rgb.z) + 16)/256;
}
PS_OUTPUT PS( VS_OUTPUT IN )
{
PS_OUTPUT outStruct;
// calc U V
float3 rgb = mapColor.SampleLevel( mapColorSampler, IN.tex, 0).rgb;
outStruct.u = ((-37.945*rgb.x - 74.494*rgb.y + 112.439*rgb.z) + 128)/256;
outStruct.v = ((112.439*rgb.x - 94.154*rgb.y - 18.285*rgb.z) + 128)/256;
// calc Y
uint half_width = (uint)(gv_rectangle.z);
uint half_height = (uint)(gv_rectangle.w);
uint cur_x = (uint)(IN.tex.x*gv_rectangle.z);
uint cur_y = (uint)(IN.tex.y*gv_rectangle.w);
uint pid = 4*(cur_y*half_width + cur_x);
outStruct.y4 = float4(CalcY(pid, 2*half_width, 2*half_height),
CalcY(pid+1, 2*half_width, 2*half_height),
CalcY(pid+2, 2*half_width, 2*half_height),
CalcY(pid+3, 2*half_width, 2*half_height));
return outStruct;
}