using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace YUV4202RGB
{
public static class YUVHelper
{
/// <summary>
/// YUV420图片字节数据保存为.bmp图片
/// </summary>
/// <param name="rgbFrame">YUV420图片数组</param>
/// <param name="width">图片宽度</param>
/// <param name="height">图片高度</param>
/// <param name="bmpFile">文件存储路径(*.bmp)</param>
public static void YUV420SaveAsBMPFile(byte[] yuv420Frame, int width, int height, string bmpFile)
{
byte[] rgbFrame = YUV420ToRGB(yuv420Frame,width,height);
// 写 BMP 图像文件。
int yu = width * 3 % 4;
int bytePerLine = 0;
yu = yu != 0 ? 4 - yu : yu;
bytePerLine = width * 3 + yu;
using (FileStream fs = File.Open(bmpFile, FileMode.Create))
{
using (BinaryWriter bw = new BinaryWriter(fs))
{
#region 文件头14字节
bw.Write('B');
bw.Write('M');
bw.Write(bytePerLine * height + 54); //文件总长度
bw.Write(0);
bw.Write(54); //图像数据地址
#endregion
#region 位图信息头40字节
bw.Write(40); //信息头长度
bw.Write(width); //位图宽度(像素)
bw.Write(height); //位图高度(像素);
bw.Write((ushort)1); // 总是1
bw.Write((ushort)24); //色深 2的24次方,即24位彩色
bw.Write(0); //压缩方式 0 不压缩
bw.Write(bytePerLine * height); //图像数据大小(字节)
bw.Write(0); //水平分辨率
bw.Write(0); //垂直分辨率
bw.Write(0); //图像使用的颜色数,0全部使用
bw.Write(0); //重要的颜色数,0全部都重要
#endregion
byte[] data = new byte[bytePerLine * height];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
data[y * bytePerLine + x * 3] = rgbFrame[bytePerLine * (height - y - 1) + x * 3 + 2]; //Blue
data[y * bytePerLine + x * 3 + 1] = rgbFrame[bytePerLine * (height - y - 1) + x * 3 + 1]; //Green
data[y * bytePerLine + x * 3 + 2] = rgbFrame[bytePerLine * (height - y - 1) + x * 3 + 0]; //Red
}
}
bw.Write(data, 0, data.Length);
bw.Flush();
}
}
}
/// <summary>
/// YUV420图片字节流转换成System.Drawing.Bitmap对象
/// </summary>
/// <param name="yuv420Frame">YUV420图片数组</param>
/// <param name="width">图片宽度</param>
/// <param name="height">图片高度</param>
/// <returns></returns>
public static System.Drawing.Bitmap YUV420FrameToImage(byte[] yuv420Frame, int width, int height)
{
byte[] rgbFrame = YUV420ToRGB(yuv420Frame, width, height);
System.Drawing.Bitmap rev = null;
// 写 BMP 图像文件。
int yu = width * 3 % 4;
int bytePerLine = 0;
yu = yu != 0 ? 4 - yu : yu;
bytePerLine = width * 3 + yu;
System.IO.Stream ms = new System.IO.MemoryStream();
using (System.IO.BinaryWriter bw = new System.IO.BinaryWriter(ms))
{
#region 文件头14字节
bw.Write('B');
bw.Write('M');
bw.Write(bytePerLine * height + 54); //文件总长度
bw.Write(0);
bw.Write(54); //图像数据地址
#endregion
#region 位图信息头40字节
bw.Write(40); //信息头长度
bw.Write(width); //位图宽度(像素)
bw.Write(height); //位图高度(像素);
bw.Write((ushort)1); // 总是1
bw.Write((ushort)24); //色深 2的24次方,即24位彩色
bw.Write(0); //压缩方式 0 不压缩
bw.Write(bytePerLine * height); //图像数据大小(字节)
bw.Write(0); //水平分辨率
bw.Write(0); //垂直分辨率
bw.Write(0); //图像使用的颜色数,0全部使用
bw.Write(0); //重要的颜色数,0全部都重要
#endregion
byte[] data = new byte[bytePerLine * height];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
data[y * bytePerLine + x * 3] = rgbFrame[bytePerLine * (height - y - 1) + x * 3 + 2]; //Blue
data[y * bytePerLine + x * 3 + 1] = rgbFrame[bytePerLine * (height - y - 1) + x * 3 + 1]; //Green
data[y * bytePerLine + x * 3 + 2] = rgbFrame[bytePerLine * (height - y - 1) + x * 3 + 0]; //Red
}
}
bw.Write(data, 0, data.Length);
bw.Flush();
ms.Seek(0, SeekOrigin.Begin);
rev = new System.Drawing.Bitmap(ms);
}
return rev;
}
/// <summary>
/// YUV420图片字节流转换成 RGB图片字节流(不含文件头及位图信息)
/// </summary>
/// <param name="yuv420Frame">YUV420图片数组</param>
/// <param name="width">图片宽度</param>
/// <param name="height">图片高度</param>
/// <returns>RGB图片字节数组</returns>
public static byte[] YUV420ToRGB(byte[] yuv420Frame, int width, int height)
{
byte[] rgb = new byte[width * height * 3];
int dIndex = 0;
for (int py = 0; py < height; py++)
{
byte[] pdata;
for (int px = 0; px < width; px++)
{
pdata = YUV420ToRGB888(yuv420Frame, width, height, px, py);
rgb[dIndex++] = pdata[0];
rgb[dIndex++] = pdata[1];
rgb[dIndex++] = pdata[2];
}
}
return rgb;
}
#region 辅助方法
/// <summary>
/// 把YUV420帧中指定位置的YUV像素转换为RGB像素
/// </summary>
/// <param name="yuv420">YUV420帧数据</param>
/// <param name="width">YUV420帧数据宽度</param>
/// <param name="height">YUV420帧数据高度</param>
/// <param name="px">像素点X坐标</param>
/// <param name="py">像素点Y坐标</param
/// >
/// <returns></returns>
private static byte[] YUV420ToRGB888(byte[] yuv420, int width, int height, int px, int py)
{
int total = width * height;
byte y, u, v;
byte[] rgb;
y = yuv420[py * width + px];
u = yuv420[(py / 2) * (width / 2) + (px / 2) + total];
v = yuv420[(py / 2) * (width / 2) + (px / 2) + total + (total / 4)];
rgb = YUV444ToRGB888(y, u, v);
return rgb;
}
/// <summary>
/// 把YUV444像素转换为RGB888像素
/// </summary>
/// <param name="Y">YUV444像素Y</param>
/// <param name="U">YUV444像素U</param>
/// <param name="V">YUV444像素V</param>
/// <returns></returns>
private static byte[] YUV444ToRGB888(byte Y, byte U, byte V)
{
byte[] rgb = new byte[3];
int C, D, E;
byte R, G, B;
//微软提供转换
C = Y - 16;
D = U - 128;
E = V - 128;
R = clip((298 * C + 409 * E + 128) >> 8);
G = clip((298 * C - 100 * D - 208 * E + 128) >> 8);
B = clip((298 * C + 516 * D + 128) >> 8);
rgb[0] = R;
rgb[1] = G;
rgb[2] = B;
return rgb;
}
private static byte clip(int p)
{
if (p < 0)
{
return 0;
} if (p > 255)
{
return 255;
}
else
{
return (byte)p;
}
}
#endregion
}
}