最近在学习冈萨雷斯的《数字图像处理》,想把整理的笔记和自己实现的小实验整理在博客上~
今天的主题是图像的灰度变换:
g(x,y)= T [ f(x,y)]
f(x,y)是输入图像,g(x,y)是处理后的图像,T是在点(x,y)邻域上定义的关于f 的一种算子。
T处理,从输入图像的左上角开始,以水平扫描的方式逐像素地处理。当该邻域的圆点位于图像的边界上时,部分邻域将位于图像的外部。此时,不是忽略外侧邻点,就是用0或者其他指定的灰度值填充图像的边缘。被填充的边界的厚度取决于邻域的大小。
以上的描述过程称为空间滤波,其中,邻域与预定义的操作一起称为空间滤波器(也称为空间掩模、核、模板或窗口)。
最小邻域的大小为1×1。在这种情况下,g 仅取决于点(x,y)处的灰度值f ,而T则成为一个形如下式的灰度(也称为灰度级或映射)变换函数:
S = T(r)
其中,为表达方便,另r和S分别表示变量,即g和f 在任意点(x,y)处的灰度。
例如,如果T(r)有如图所示的形式
对f 中每一个像素施以变换产生相应的g的像素的效果将比原图像有更高的对比度。这种技术被称为对比度拉伸。
极限情况下,会产生二级(二值)图
图像增强技术是面向问题的,没有通用的“理论”。
一些基本的灰度变换函数:
①图像反转
S = L - 1 - r
通俗的说,就是图像的黑边反转交换。
特别适用于增强嵌入在一幅图像的暗区域中的白色或灰色细节。
②对数变换
S = c*log(1+ r)
改变换将输入中范围较窄的低灰度值映射为输出中较宽范围的灰度值,对范围较宽的高输入灰度值映射为输出中较窄范围的灰度值。
我们一般使用这种类型的变换来扩展图像中的暗像素值,同时压缩更高灰度级的值。
反对数变换的作用与此正好相反。
对数函数的一般形状的任何曲线,都能完成图像的灰度级的扩展和压缩,但是一会介绍的幂律变换对于这个目的更为通用。
对数函数有个重要的特征,即它压缩像素值变换较大的图像的动态范围。这一特征一般被应用于傅里叶频谱中。
通常,频谱值的范围从0到10^6,甚至更高。尽管计算机能毫无疑问的处理这一范围的数字,但图像的显示系统通常不能如实地再现如此大范围的灰度值。因而,最终结果是许多重要的灰度细节在典型的傅里叶频谱的显示中丢失了。
如果我们先将这些频谱进行对数函数的对比度拉伸,再线性的缩放到新的值域,并在同一个8比特显示系统中显示频谱的结果。会看到很多丰富的细节。
③幂律(伽马)变换
S = c* r ^γ
习惯上,幂律方程中的指数被称为伽马
用于校正幂律响应现象的处理称为伽马校正
幂律变换与对数变换一样,也可以扩展和压缩一些灰度级,且应用的更为广泛。
④分段线性变换函数
优点:形式可以是任意复杂的。
缺点:技术说明要求用户输入。
上面就是关于灰度变换的一些总结。
下面我想做一个小实验,对一幅光照不均匀的图像,进行一下伽马变换的对比度拉伸,调整一下显示的亮度。
我是用VB.NET的Emgu来实现的。
这个是原图
'伽马变换的灰度拉伸 '处理光照不均匀的图像 Dim img As New Image(Of Gray, Byte)("C:\test.bmp") Dim height As Integer = img.Height Dim width As Integer = img.Width Dim gama As Double = 0.4 Dim Sbigest As Integer = 0 Dim Sfewest As Integer = 255 For i = 0 To height - 1 For j = 0 To width - 1 If img.Data(i, j, 0) < Sfewest Then Sfewest = img.Data(i, j, 0) End If If img.Data(i, j, 0) > Sbigest Then Sbigest = img.Data(i, j, 0) End If Next Next For i = 0 To height - 1 For j = 0 To width - 1 img.Data(i, j, 0) = (img.Data(i, j, 0) ^ gama - Sfewest ^ gama) / (Sbigest ^ gama - Sfewest ^ gama) * 255 Next Next img.Save("C:\test-result.bmp")
2015.9.3补充~
最近细致的学习了灰度变换以及直方图的一些相关的处理,回过头来,想把这篇博文中的那张图处理下。
之前的实验结果,伽马变换后的图片,四个角的部位都有阴影。这是因为那些部分的灰度值都较小,图像较暗,伽马变换后也难以校正。
如果想校正这部分区域,就需要考虑对比度的问题,才能对我们想要的部分进行有效的增强。
因此我想求出整体的方差与局部的方差。如果局部的方差大于整体方差,则认为是文字与背景的交界区域。否则,则认为是背景。
以下是代码实现。放假在家没有VS,只好用matlab实现的~
clear all; close all; clc; I = imread('C:\test\src.bmp'); I = rgb2gray(I); [height,width] = size(I); E1 = 1.6; E2 = 10; k0 = 0.9; k1 = 0.8; k2 = 0.2; m = 0; for i=1:height for j=1:width m = m + double(I(i,j)); end end %灰度级平均值 m = m / (height * width); variance = 0; for i=1:height for j=1:width variance = variance + (double(I(i,j) - m))^2; end end %灰度方差 variance = variance / (height * width); I2 = I; for i=1:height for j=1:width m1 = 0; for h1 = i - 3: i + 3 for h2 = j - 3: j + 3 if(h1)<1 || (h2)<1 || (h1)>height || (h2)>width m1 = m1 + m; else m1 = m1 + double(I(h1,h2)); end end end m1 = m1 / 49; variance1 = 0; for h1 = i - 3: i + 3 for h2 = j - 3: j + 3 if(h1)<1 || (h2)<1 || (h1)>height || (h2)>width variance1 = variance1 + (m1 - m)^2; else variance1 = variance1 + (double(I(h1,h2)) - m1)^2; end end end variance1 = variance1 / 49; if (variance1 >= k1 * variance) && (m1 <= I(i,j)) %方差大于平均方差 %该点处的灰度值大于邻域的平均灰度值 I2(i,j) = E1*I(i,j) ; elseif (variance1 >= k1 * variance) && (m1 > I(i,j)) elseif (variance1 < k2 * variance) I2(i,j) = 2*E2*I(i,j) ; else end end end imwrite(I2,'C:/test/test_result.bmp','bmp');
图片边上的黑边是因为邻域的选取造成的。