这三个概念是非常容易混淆的,必须能够深刻理解这三个概念
首先在内存上:
像素≥字节>位
字节由位组成,一般一个字节是八位
每一位的数只有两种表达方式 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];
}
///
/// 获取像素的数组
///
///
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++;
}
}
///
/// 彩色转灰色具体的实现函数
///
/// BitmapImage
///
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);
}
///
/// 获取灰色数组
///
///
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;
}
///
/// 灰色转换的具体公式
///
///
///
///
///
public double Rgb2Gray(long r, long g, long b)
{
return (r*0.299+g*0.587+b*0.114); //只针对正常情况
}
}
}
GitHub代码
https://github.com/Mushano/WPF-ColoerImageToGray