RAW图文件是仅包含图像数据的文件,它是由图像传感器拍摄得到的最初始的图像数据。
RAW图一个像素只包含一个通道的颜色信息,所以解析出来是灰色的图像,要得到彩色图像,还需要根据RAW图的颜色滤波阵列进行插值,进行去马赛克处理。
解析RAW图文件首先打开并读取到文件的比特流,下面以位深度为10位的RAW图位例:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ReadRAW
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public byte[] ImgData= null;
public int[] ImgInt = null;
public byte[] ImgByte= null;
public byte[,] ImgRGB= null;
public Bitmap Bmp = null;
public int Width = 1920;
public int Height = 1080;
public string Bits = "10";
//打开RAW图按钮
private void LoadRaw_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "请选择文件";
ofd.Filter = "RAW文件(*.raw)|*.raw|所有文件(*.*)|*.*";
ofd.FilterIndex = 1;
if (ofd.ShowDialog() == DialogResult.OK)
{
ImgData = File.ReadAllBytes(ofd.FileName);
ImgInt = GetImgInt(ImgData, Width, Height, Bits);
ImgByte = GetImgByte(ImgInt, Width, Height, Bits);
ImgRGB = GetImgRGB(ImgByte, Width, Height);
Bmp = GetBmp(ImgRGB, Width, Height);
if (Bmp!= null)
{
pictureBox1.Image = new Bitmap(Bmp, pictureBox1.Width, pictureBox1.Height);
}
}
}
}
}
根据图像位深度和宽度、高度将RAW图比特流转换为整型数组:
// 文件字节流解析,10位的图是两个字节转为一个整形数据
public int[] GetImgInt(byte[] img, int width, int height, string Bits)
{
if (img == null)
{
return null;
}
int Index = 0;
int[] OriImg = new int[width * height];
try
{
if (Bits == "8")
{
if (img.Length != width * height)
{
return null;
}
for (int i = 0; i < img.Length; i += 4)
{
OriImg[i] = img[i];
OriImg[i + 1] = img[i + 1];
OriImg[i + 2] = img[i + 2];
OriImg[i + 3] = img[i + 3];
}
return OriImg;
}
else if (Bits == "10" || Bits == "12")
{
if (img.Length != width * height * 2)
{
return null;
}
//图像的像素个数一定是4的倍数,所以一次性处理4个像素,循环展开,提高循环效率
for (int i = 1; i < img.Length; i += 8)
{
OriImg[Index] = (0x00FF & img[i - 1]) | (0xFF00 & img[i] << 8);
Index++;
OriImg[Index] = (0x00FF & img[i + 1]) | (0xFF00 & img[i + 2] << 8);
Index++;
OriImg[Index] = (0x00FF & img[i + 3]) | (0xFF00 & img[i + 4] << 8);
Index++;
OriImg[Index] = (0x00FF & img[i + 5]) | (0xFF00 & img[i + 6] << 8);
Index++;
}
return OriImg;
}
}
finally
{
GC.Collect();
}
return null;
}
由于位深度为10的图像灰阶范围为0~1023,为了能在显示器上显示,需要将图像整型数组转化为位深度为8的图像数组。
// 将整型数组转为8位图像数据
public byte[] GetImgByte(int[] img, int width, int height, string Bits)
{
if (img == null)
{
return null;
}
byte[] Img = new byte[img.Length];
try
{
switch (Bits)
{
case ("8"):
for (int i = 0; i < img.Length; i += 4)
{
Img[i] = (byte)img[i];
Img[i + 1] = (byte)img[i + 1];
Img[i + 2] = (byte)img[i + 2];
Img[i + 3] = (byte)img[i + 3];
}
break;
case ("10"):
for (int i = 0; i < img.Length; i += 4)
{
Img[i] = (byte)(img[i] >> 2);
Img[i + 1] = (byte)(img[i + 1] >> 2);
Img[i + 2] = (byte)(img[i + 2] >> 2);
Img[i + 3] = (byte)(img[i + 3] >> 2);
}
break;
case ("12"):
for (int i = 0; i < img.Length; i += 4)
{
Img[i] = (byte)(img[i] >> 4);
Img[i + 1] = (byte)(img[i + 1] >> 4);
Img[i + 2] = (byte)(img[i + 2] >> 4);
Img[i + 3] = (byte)(img[i + 3] >> 4);
}
break;
}
}
catch (Exception) {; }
finally
{
GC.Collect();
}
return Img;
}
将8位深度的数组转为具有RGB三个通道信息的图像数组:
//byte[]转为RGB图像矩阵byte[3,]
public byte[,] GetImgRGB(byte[] img, int width, int height)
{
if (img == null)
{
return null;
}
byte[,] Img = new byte[3, width * height];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
Img[0, y * width + x] = img[y * width + x];
Img[1, y * width + x] = img[y * width + x];
Img[2, y * width + x] = img[y * width + x];
}
}
return Img;
}
将图像矩阵转化为Bitmap位图:
// RGB图像矩阵转为Bitmap
public Bitmap GetBmp(byte[,] img, int width, int height)
{
if (img == null || width * height * 3 != img.Length)
{
return null;
}
Bitmap bt = new Bitmap(width, height);
BitmapData data = bt.LockBits(new Rectangle(new Point(), bt.Size), ImageLockMode.WriteOnly, bt.PixelFormat);
int ofs = 0;
int pix;
try
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
pix = Color.FromArgb(img[0, y * width + x], img[1, y * width + x], img[2, y * width + x]).ToArgb();
Marshal.WriteInt32(data.Scan0, ofs, pix);
ofs += 4;
}
}
}
catch (EndOfStreamException)
{
}
finally
{
bt.UnlockBits(data);
}
return bt;
}
显示出来的图像是灰度图像,效果如下所示:
若想得到彩色图像,需要加入插值算法去马赛克。