图像处理(3)_灰度分布均衡化

来源:互联网 发布:iphone7屏幕录制软件 编辑:程序博客网 时间:2024/06/05 20:04

灰度分布均衡化又称直方图均衡化。

其理论原理见:http://zh.wikipedia.org/wiki/%E7%9B%B4%E6%96%B9%E5%9B%BE%E5%9D%87%E8%A1%A1%E5%8C%96

其实维基百科是一个很不错的东西。大家有问题的话可以去维基百科逛逛。大笑 我比较懒,理论的话全引自其他网页。

好了开始进入正题。

来看一个灰度图像,让n_i表示灰度i出现的次数,这样图像中灰度为i 的像素的出现概率是

p_x(i) = \frac{n_i}{n}, i\in {0,..., L - 1}

L 是图像中所有的灰度数,n 是图像中所有的像素数,p 实际上是图像的直方图,归一化到0..1

c 作为对应于p 的累计概率函数, 定义为:

 c(i) = \sum_{j=0}^i p_x(j)

c 是图像的累计归一化直方图。

我们创建一个形式为 y = T(x) 的变化,对于原始图像中的每个值它就产生一个y,这样y 的累计概率函数就可以在所有值范围内进行线性化,转换公式定义为:

y_i = T(x_i) = c(i)

注意 T 将不同的等级映射到 {0..1} 域,为了将这些值映射回它们最初的域,需要在结果上应用下面的简单变换:

y_i' = y_i \cdot(max - min) + min

上面描述了灰度图像上使用直方图均衡化的方法,但是通过将这种方法分别用于图像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  处理完毕。