灰度分布均衡化又称直方图均衡化。
其理论原理见:http://zh.wikipedia.org/wiki/%E7%9B%B4%E6%96%B9%E5%9B%BE%E5%9D%87%E8%A1%A1%E5%8C%96
其实维基百科是一个很不错的东西。大家有问题的话可以去维基百科逛逛。 我比较懒,理论的话全引自其他网页。
好了开始进入正题。
来看一个灰度图像,让表示灰度出现的次数,这样图像中灰度为 的像素的出现概率是
是图像中所有的灰度数, 是图像中所有的像素数, 实际上是图像的直方图,归一化到。
把 作为对应于 的累计概率函数, 定义为:
是图像的累计归一化直方图。
我们创建一个形式为 的变化,对于原始图像中的每个值它就产生一个,这样 的累计概率函数就可以在所有值范围内进行线性化,转换公式定义为:
注意 T 将不同的等级映射到 域,为了将这些值映射回它们最初的域,需要在结果上应用下面的简单变换:
上面描述了灰度图像上使用直方图均衡化的方法,但是通过将这种方法分别用于图像RGB颜色值的红色、绿色和蓝色分量,从而也可以对彩色图像进行处理。
上述是维基百科对直方图均衡化的原理解释,转化为算法描述则是:
(1)获取目标图像的直方图。
(2)从获取直方图图中获取灰度密度分布。【就是每个灰度级 像素个数/ 灰度直方图中像素的总个数】
(3)对密度直方图进行累加,每个灰度级密度是前面所有灰度级的密度总和。
(4)对原图像进行遍历然,每个像素值为累加后的概率密度索引值 并乘上255 对图像进行还原。
代码:
public static Bitmap GrayBalanceChange(Bitmap tp)
{
int w = tp.Width;
int h = tp.Height;
int i, j,T,sum;
byte[] ky = new byte[w * h];
byte[] hist = new byte[256]; // 用于记录灰度分布
double[] phist = new double[256]; // 用于记录灰度分布的概率分布
ky = ChangeByte(tp); // 获取图像一维数组
hist = GetGrayHist(tp); // 获取直方图
sum = 0;
for (i = 0; i < 256; i++)
{
sum += hist[i]; // 求直方图总像素个数
}
// 获取灰度分布的概率分布
for (i = 0; i < 256; i++)
{
phist[i] = (double)(hist[i]) / (sum);
}
// 对灰度概率累加求和
for (i = 1; i < 255; i++) // 对第一个和最后一个灰度级不处理
{
phist[i] = phist[i] + phist[i - 1];
}
for (i = 0; i < 256; i++) // 累加后还原成直方图
{
phist[i] = 255 * phist[i];
}
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
T = ky[i * w + j]; // 原图像的灰度级
ky[i * w + j] = (byte)(phist[T]); // 将原图灰度级作为索引赋给原图像素
}
}
tp = ChangeBitmap(ky, tp); // 将一维数组转换为bitmap
return tp;
}
下面是一维数组转换为bitmap
public static byte[] ChangeByte(Bitmap tp)
{
int w = tp.Width; // 图像实际宽度
int h = tp.Height; // 图像实际高度
BitmapData srcdata = tp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly,
PixelFormat.Format24bppRgb); // 将图像锁入内存,只读,图像格式为rgb格式
byte[] pixeData = new byte[h * w]; // 声明一个与图像等大的数组,因为每个像素点的(灰度)值
// 范围为 0 - 255 所以使用byte数组
int sride = srcdata.Stride; // 获取图像的系统宽度
unsafe // C#使用指针时需要在unsafe中使用,
{ // C#默认不支持unsafe,使用时在项目 -》选项 -》生成中设置
byte* temp = (byte*)srcdata.Scan0.ToPointer(); // 获取图像锁入内存的首地址
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
pixeData[i * w + j] = temp[0]; // 我们只获取第一个色彩的值,对于灰度图像
// red blue green 像素值相等
temp += 3; // 跳过其余两色
}
temp += sride - w * 3; // 加上系统对齐的宽度
}
}
tp.UnlockBits(srcdata); // 解锁
return pixeData;
}
效果:
原图 处理后图像
OK 处理完毕。