这三个概念是非常容易混淆的,必须能够深刻理解这三个概念
首先在内存上:
像素≥字节>位
字节由位组成,一般一个字节是八位
每一位的数只有两种表达方式 0或1
所以每一个字节的可能性有 2^8种,即256
字节的取值为0-255
而像素则是根据不同的像素格式,占有的字节数也不同,
如RGB32格式就是每个像素占4个字节
常见的有RBG32,RGB555等
像素格式表示图像每个像素的数值分布情况。
主要有 R G B 红绿蓝三个颜色通道
在排布的时候是按照BGR的顺序进行排列
如RGB32像素格式:
BGRA BGRA BGRA
A该字节不表示,所以 BGR每个颜色通道各占8个字节,一共有256中可能。
直接利用现有函数对图像的格式进行转换就可以实现彩色转灰色
FormatConvertedBitmap myFormatedConvertedBitmap = new FormatConvertedBitmap(); myFormatedConvertedBitmap.BeginInit(); myFormatedConvertedBitmap.Source = bi; myFormatedConvertedBitmap.DestinationFormat = PixelFormats.Gray8; myFormatedConvertedBitmap.EndInit(); GreyImageCtr.Source = myFormatedConvertedBitmap; TabControl1.SelectedIndex = 1;
FormatConvertedBitmap类是BitmapSource下的子类
图像读入位图中的类BitmapImage也是FormatConvertedBitmap的子类
利用上述方法就可以直接把彩色图像转化为灰色图像
在C#中一般不用指针操作。但是特殊情况需要使用的时候需要用到unsafe
在用unsafe的时候需要先在项目里面设置解决方案的属性,允许使用不安全代码。
以下代码看不懂可以别看
protected override void DecodeImage(WriteableBitmap source) { try { source.Lock(); unsafe { byte* pBackBuffer = (byte*)source.BackBuffer; int currentPixelIndex = 0; //当前解码像素索引 byte[] r = R; byte[] g = G; byte[] b = B; int tailAddressOffset = GetRowTailAddressOffset(); for (int row = 0; row < Rows; row++) { for (int column = 0; column < Columns; column++) { b[currentPixelIndex] = *pBackBuffer++; g[currentPixelIndex] = *pBackBuffer++; r[currentPixelIndex++] = *pBackBuffer++; } /*for (int offset = 0; offset < tailAddressOffset; offset++) pBackBuffer++;*/ pBackBuffer = pBackBuffer + tailAddressOffset; } } } finally { source.Unlock(); } } protected WriteableBitmap Color2Gray(byte[] r, byte[] g, byte[] b, ResultDataTypeEnum resultType) { WriteableBitmap wb = null; switch(resultType) { case ResultDataTypeEnum.Byte: case ResultDataTypeEnum.Default: { byte[] gray = new byte[r.Length]; for(int i=0;i<gray.Length;i++) { gray[i] = (byte)RGB2Gray64(r[i], g[i], b[i],0); } wb = CreateWB(ImageEncoder.Gray8.Encode(gray), PixelFormats.Gray8); break; } case ResultDataTypeEnum.Short: { short[] gray = new short[r.Length]; for (int i = 0; i < gray.Length; i++) { gray[i] = (short)RGB2Gray64(r[i], g[i], b[i],8); } wb = CreateWB(ImageEncoder.Gray16.Encode(gray), PixelFormats.Gray16); break; } case ResultDataTypeEnum.Int: { int[] gray = new int[r.Length]; for (int i = 0; i < gray.Length; i++) { gray[i] = (int)RGB2Gray64(r[i], g[i], b[i],24); } wb = CreateWB(ImageEncoder.Gray32.Encode(gray), PixelFormats.Gray32Float); break; } } return wb; } private long RGB2Gray64(long r, long g, long b, int scale) { //gray = r*0.299+g*0.587+b*0.114 系数扩大了16384倍,即左移的14位 return ((r << scale) * 4899 + (g << scale) * 9617 + (b << scale) * 1868) >> 14; } protected WriteableBitmap CreateWB(byte[] imageData, PixelFormat pf) { WriteableBitmap wb = new WriteableBitmap(Columns, Rows, DPIX, DPIY, pf, null); Int32Rect rect = new Int32Rect(0, 0, wb.PixelWidth, wb.PixelHeight); int stride = wb.PixelWidth * pf.BitsPerPixel / 8; wb.WritePixels(rect, imageData, stride, 0); return wb; }
在上述我们知道,RGB32格式的像素中数据的排列方式是
BGRA BGRA BGRA ……
要转换为灰度图像,我们需要分别得到B,G,R三个数组,然后利用公式
计算得到一个新的灰色图像数值的数组
计算公式为:
r0.299+g0.587+b*0.114
算法的思维导图如下:
点击灰色处理会自动跳到灰色图像的TabControl窗口
方法
TabControl1.SelectedIndex = 1
using System.Windows.Media; using System.Windows.Media.Imaging; namespace 数字图像处理1 { public class ImageHandler { public int Rows; public int Columns; public PixelFormat f; public byte[] R; public byte[] G; public byte[] B; public ImageHandler(BitmapImage image) { Rows = image.PixelWidth; Columns = image.PixelHeight; f = image.Format; int s = Columns * f.BitsPerPixel/8; R = new byte[Rows * s/4]; G = new byte[Rows * s/4]; B = new byte[Rows * s/4]; } /// <summary> /// 获取像素的数组 /// </summary> /// <param name="source"></param> public void GetPixelArray(byte[] g) { int i = 0; int bi = 0; int gi = 0; int ri = 0; while (i<g.Length) { switch (i%4) { case 0: B[bi] = g[i]; bi++; break; case 1: G[gi] = g[i]; gi++; break; case 2: R[ri] = g[i]; ri++; break; default: break; } i++; } } /// <summary> /// 彩色转灰色具体的实现函数 /// </summary> /// <param name="source">BitmapImage</param> /// <returns></returns> public BitmapSource Color2Gray(BitmapImage ima) { WriteableBitmap source = new WriteableBitmap(ima.PixelWidth, ima.PixelHeight, ima.DpiX, ima.DpiY, ima.Format, ima.Palette); int _pixelWidth = source.PixelWidth; int _pixelheight = source.PixelHeight ; double _dpiX = source.DpiX; double _dpiY = source.DpiY; BitmapPalette _p = source.Palette; int _stride = source.PixelWidth * source.Format.BitsPerPixel/8; //测试 byte[] g = new byte[ima.PixelHeight * _stride]; ima.CopyPixels(g, _stride, 0); GetPixelArray(g); byte[] gray = GetGrayArray(); return BitmapSource.Create(_pixelWidth, _pixelheight, _dpiX, _dpiY, PixelFormats.Gray8, _p, gray, _stride/4); } /// <summary> /// 获取灰色数组 /// </summary> /// <returns></returns> public byte[] GetGrayArray() { byte[] gray = new byte[B.Length]; for (int i = 0; i < B.Length; i++) { gray[i] = (byte)Rgb2Gray(R[i], G[i], B[i]); } return gray; } /// <summary> /// 灰色转换的具体公式 /// </summary> /// <param name="r"></param> /// <param name="g"></param> /// <param name="b"></param> /// <returns></returns> public double Rgb2Gray(long r, long g, long b) { return (r*0.299+g*0.587+b*0.114); //只针对正常情况 } } }
GitHub代码