目录
- 一、图像噪声
- 1、高斯噪声
- 2、椒盐噪声
- 3、泊松噪声
- 4、乘性噪声
- 5、瑞利噪声
- 6、伽马噪声
- 二、图像滤波
- 三、各种滤波器
- 1、均值滤波
- 2、中值滤波
- 3、最大最小值滤波
- 4、引导滤波
- 四、图像增强
- 1、点处理
- 1、线性变换
- 2、分段线性变换
- 3、对数变换
- 4、幂律变换/伽马变换
- 2、领域处理
- 3、图像增强常用方法
一、图像噪声
图像噪声是图像在获取或是传输过程中受到随机信号干扰,妨碍人们对图像理解及分析处理的信号。
图像噪声的产生来自图像获取中的环境条件和传感元器件自身的质量,图像在传输过程中产生图像噪声的主要因素是所用的传输信道受到了噪声的污染。
1、高斯噪声
高斯噪声是指它的概率密度函数服从高斯分布的一类噪声。
如果一个噪声,它的幅度分布服从高斯分布,而它的任意两个采样样本之间不相关,则称它为高斯白噪声。
必须区分高斯噪声和白噪声两个不同的概念。
高斯噪声是指噪声的概率密度函数服从高斯分布,白噪声是指噪声的任意两个采样样本之间不相关,两者描述的角度不同。
白噪声不必服从高斯分布,高斯分布的噪声不一定是白噪声。
高斯分布
产生原因
- 图像传感器在拍摄时不够明亮、亮度不够均匀。
- 电路各元器件自身噪声和相互影响。
- 图像传感器长期工作,温度过高。
一个正常的高斯采样分布公式,得到输出像素 Pout。
Pout(输出像素) = Pin(输入像素) + random.gauss(符合高斯分布的随机数)
其中 random.gauss 是通过 sigma 和 mean 来生成符合高斯分布的随机数。
给一副数字图像加上高斯噪声的处理顺序如下:
1、输入参数 sigma 和 mean。
2、生成高斯随机数。
3、根据输入像素计算出输出像素。
4、重新将像素值放缩在[0 ~ 255]之间。
5、循环所有像素。
6、输出图像。
import cv2 # 导入 OpenCV 库,用于图像处理
import random # 导入 random 库,用于生成随机数
# 定义添加高斯噪声的函数
def GaussianNoise(src, means, sigma, percentage):
# 将输入图像赋值给 NoiseImg 变量,后续操作在该变量上进行
NoiseImg = src
# 计算要添加的噪声点数
NoiseNum = int(percentage * src.shape[0] * src.shape[1])
# 在图像中随机选择位置添加噪声
for i in range(NoiseNum):
# 生成随机的行坐标
randX = random.randint(0, src.shape[0] - 1) # random.randint 生成随机整数,高斯噪声图片边缘不处理,故-1
# 生成随机的列坐标
randY = random.randint(0, src.shape[1] - 1)
# 通过高斯分布生成随机噪声并添加到图像像素值
NoiseImg[randX, randY] = NoiseImg[randX, randY] + random.gauss(means, sigma)
# 防止像素值越界
# 如果添加噪声后的像素值小于0,则将像素值截断为0
if NoiseImg[randX, randY] < 0:
NoiseImg[randX, randY] = 0
# 如果添加噪声后的像素值大于255,则将像素值截断为255
elif NoiseImg[randX, randY] > 255:
NoiseImg[randX, randY] = 255
# 返回添加噪声后的图像
return NoiseImg
# 读取灰度图像
img = cv2.imread('img/lenna.png', 0)
# 调用添加高斯噪声的函数,对 80% 的像素点加噪
img1 = GaussianNoise(img, 2, 4, 0.8)
# 读取彩色图像
img = cv2.imread('img/lenna.png')
# 将彩色图像转换为灰度图像
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 将添加噪声后的图像保存为文件
# cv2.imwrite('lenna_GaussianNoise.png', img1)
# 显示原始灰度图像
cv2.imshow('source', img2)
# 添加噪声后的图像
cv2.imshow('lenna_GaussianNoise', img1)
# 等待用户按键
cv2.waitKey(0)
2、椒盐噪声
椒盐噪声又称为脉冲噪声,它是一种随机出现的白点或者黑点。
椒盐噪声 = 椒噪声 (pepper noise)+ 盐噪声(salt noise)。 椒盐噪声的值为0(椒)或者255(盐)。
椒噪声是低灰度噪声,盐噪声属于高灰度噪声。一般两种噪声同时出现,呈现在图像上就是黑白杂点。
对于彩色图像,也有可能表现为在单个像素 BGR 三个通道随机出现的255或0。
如果通信时出错,部分像素的值在传输时丢失,就会发生这种噪声。
椒盐噪声的成因可能是影像讯号受到突如其来的强烈干扰而产生等。例如失效的感应器导致像素值为最小值,饱和的感应器导致像素值为最大值。
给一副数字图像加上椒盐噪声的处理顺序如下:
1、指定信噪比 SNR(信号和噪声所占比例) ,其取值范围在[0, 1]之间。
2、计算总像素数目 SP, 得到要加噪的像素数目 NP = SP * SNR。
3、随机获取要加噪的每个像素位置P(i, j)。
4、指定像素值为255或者0。
5、重复3, 4两个步骤完成所有 NP 个像素的加噪。
import cv2 # 导入 OpenCV 库,用于图像处理
import random # 导入 random 库,用于生成随机数
# 定义椒盐噪声函数
def fun1(src, percentage):
# 将输入图像赋值给 NoiseImg 变量,后续操作在该变量上进行
NoiseImg = src
# 计算要添加的椒盐噪声点数
NoiseNum = int(percentage * src.shape[0] * src.shape[1])
# 在图像中随机选择位置添加椒盐噪声
for i in range(NoiseNum):
# 生成随机的行坐标
randX = random.randint(0, src.shape[0] - 1) # random.randint 生成随机整数,椒盐噪声图片边缘不处理,故-1
# 生成随机的列坐标
randY = random.randint(0, src.shape[1] - 1)
# 以50%的概率将像素值设为0或255,模拟椒盐噪声
if random.random() <= 0.5:
NoiseImg[randX, randY] = 0
else:
NoiseImg[randX, randY] = 255
# 返回添加噪声后的图像
return NoiseImg
# 读取灰度图像
img = cv2.imread('img/lenna.png', 0)
# 调用椒盐噪声函数,对 20% 的像素点加噪
img1 = fun1(img, 0.2)
# 将添加椒盐噪声后的图像保存为文件
# cv2.imwrite('lenna_PepperandSalt.png', img1)
# 读取彩色图像
img = cv2.imread('img/lenna.png')
# 将彩色图像转换为灰度图像
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 显示原始灰度图像
cv2.imshow('source', img2)
# 显示添加椒盐噪声后的图像
cv2.imshow('lenna_PepperandSalt', img1)
# 等待用户按键
cv2.waitKey(0)
3、泊松噪声
符合泊松分布的噪声模型,泊松分布适合于描述单位时间内随机事件发生的次数的概率分布。
如某一服务设施在一定时间内受到的服务请求的次数,电话交换机接到呼叫的次数、汽车站台的候客人数、机器出现的故障数、自然灾害发生的次数、DNA 序列的变异数、放射性原子核的衰变数等等。
时间越长,事件发生的可能越大,且不同时间内发生该事件的概率是相互独立的。
对于非常短的一段时间来说,出现该时间两次的概率几乎为零。
一开始的时候事件没有发生过。
P [ ( N ( t + T ) − N ( t ) ) = k ] = e − λ T ( λ T ) k k ! k = 0 , 1 , . . . P[(N(t+T)-N(t))=k] = \frac{e^{-\lambda T}(\lambda T)^k}{k!}\quad k=0,1,... P[(N(t+T)−N(t))=k]=k!e−λT(λT)kk=0,1,...
e 是自然对数的底。
λ 是事件发生的平均速率(每单位时间的平均事件发生次数)。
t 代表了观察事件的起始时间
T 是时间间隔。
k 是实际在时间间隔 T 内发生的事件次数。
k! 表示k的阶乘。
import cv2 as cv # 导入 OpenCV 库,用于图像处理
from skimage import util # 从 skimage 库中导入 util 模块,用于添加噪声
'''
def random_noise(image, mode='gaussian', seed=None, clip=True, **kwargs):
功能:为浮点型图片添加各种随机噪声
参数:
image:输入图片(将会被转换成浮点型),ndarray 型
mode: 可选择,str 型,表示要添加的噪声类型
gaussian:高斯噪声
localvar:高斯分布的加性噪声,在“图像”的每个点处具有指定的局部方差。
poisson:泊松噪声
salt:盐噪声,随机将像素值变成1
pepper:椒噪声,随机将像素值变成0或-1,取决于矩阵的值是否带符号
s&p:椒盐噪声
speckle:均匀噪声(均值 mean 方差 variance),out=image+n*image
seed:可选的,int 型,如果选择的话,在生成噪声前会先设置随机种子以避免伪随机
clip:可选的,bool 型,如果是 True,在添加均值,泊松以及高斯噪声后,会将图片的数据裁剪到合适范围内。如果是 False,则输出矩阵的值可能会超出[-1,1]
mean:可选的,float 型,高斯噪声和均值噪声中的 mean 参数,默认值=0
var: 可选的,float 型,高斯噪声和均值噪声中的方差,默认值=0.01(注:不是标准差)
local_vars:可选的,ndarry 型,用于定义每个像素点的局部方差,在 localvar 中使用
amount: 可选的,float 型,是椒盐噪声所占比例,默认值=0.05
salt_vs_pepper:可选的,float 型,椒盐噪声中椒盐比例,值越大表示盐噪声越多,默认值=0.5,即椒盐等量
--------
返回值:ndarry 型,且值在[0,1]或者[-1,1]之间,取决于是否是有符号数
'''
# 读取图像文件
img = cv.imread("img/lenna.png")
# 生成随机噪声并叠加到原始图像上,poisson 为泊松分布
noise_gs_img = util.random_noise(img, mode='poisson')
# 显示原始图像
cv.imshow("source", img)
# 显示加入噪声的图像
cv.imshow("lenna", noise_gs_img)
# 保存带有噪声的图像
# cv.imwrite('lenna_noise.png', noise_gs_img)
# 等待用户按键操作,参数为0表示无限等待
cv.waitKey(0)
# 关闭所有图像窗口
cv.destroyAllWindows()
4、乘性噪声
一般由信道不理想引起,它们与信号的关系是相乘,信号在它在,信号不在它也就不在。
5、瑞利噪声
相比高斯噪声而言,其形状向右歪斜,这对于拟合某些歪斜直方图噪声很有用。
瑞利噪声的实现可以借由平均噪声来实现。
6、伽马噪声
其分布服从了伽马曲线的分布。
伽马噪声的实现,需要使用 b 个服从指数分布的噪声叠加而来。指数分布的噪声,可以使用均匀分布来实现。
b=1 时为指数噪声,b>1 时通过若干个指数噪声叠加,得到伽马噪声。
二、图像滤波
图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。
消除图像中的噪声成分叫作图像的平滑化或滤波操作。信号或图像的能量大部分集中在幅度谱的低频和中频段是很常见的,而在较高频段,感兴趣的信息经常被噪声淹没。因此一个能降低高频成分幅度的滤波器就能够减弱噪声的影响。
平滑滤波是低频增强的空间域滤波技术。它的目的有两类:一类是模糊;另一类是消除噪音。空间域的平滑滤波一般采用简单平均法进行,就是求邻近像元点的平均亮度值。邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好,但邻域过大,平滑会使边缘信息损失的越大,从而使输出的图像变得模糊,因此需合理选择邻域的大小。
关于滤波器,一种形象的比喻法是:可以把滤波器想象成一个包含加权系数的窗口,当使用这个滤波器平滑处理图像时,就把这个窗口放到图像之上,透过这个窗口来看我们得到的图像。
目的
1、消除图像中混入的噪声。
2、为图像识别抽取出图像特征。
要求
1、不能损坏图像轮廓及边缘。
2、图像视觉效果应当更好。
三、各种滤波器
1、均值滤波
均值滤波,是图像处理中最常用的手段,从频率域观点来看均值滤波是一种低通滤波器,高频信号将会去掉,因此可以帮助消除图像尖锐噪声,实现图像平滑,模糊等功能。理想的均值滤波是用每个像素和它周围像素计算出来的平均值替换图像中每个像素。
g
(
x
,
y
)
=
1
M
∑
f
∈
s
f
(
x
,
y
)
g(x,y)=\frac{1}{M} \sum_{f∈s}f(x,y)
g(x,y)=M1f∈s∑f(x,y)
- 从左到右从上到下计算图像中的每个像素,最终得到处理后的图像。
- 均值滤波可以加上两个参数,即迭代次数,Kernel 数据大小。
- 一个相同的 Kernel,但是多次迭代就会效果越来越好。
- 同样,迭代次数相同,Kernel 矩阵越大,均值滤波的效果就越明显。
注意,这个 Kernel 加权求和之后还得除以9才是均值,用均值替换蓝色中心像素。
优点:算法简单,计算速度快。
缺点:降低噪声的同时使图像产生模糊,特别是景物的边缘和细节部分。
2、中值滤波
中值滤波也是消除图像噪声最常见的手段之一,特别是消除椒盐噪声,中值滤波的效果要比均值滤波更好。
中值滤波跟均值滤波唯一不同是,不是用均值来替换中心每个像素,而是将周围像素和中心像素排序以后,取中值。
一个 3X3 大小的中值滤波如下:
优点:抑制效果很好,画面的清析度基本保持。
缺点:对高斯噪声的抑制效果不是很好。
3、最大最小值滤波
最大最小值滤波是一种比较保守的图像处理手段,与中值滤波类似,首先要排序周围像素和中心像素值,然后将中心像素值与最小和最大像素值比较,如果比最小值小,则替换中心像素为最小值,如果中心像素比最大值大,则替换中心像素为最大值。
一个 Kernel 矩阵为 3X3 的最大最小值滤波如下:
4、引导滤波
在引导滤波的定义中,用到了局部线性模型。
该模型认为,某函数上一点与其邻近部分的点成线性关系,一个复杂的函数就可以用很多局部的线性函数来表示,当需要求该函数上某一点的值时,只需计算所有包含该点的线性函数的值并做平均即可。这种模型,在表示非解析函数上,非常有用。
同理,我们可以认为图像是一个二维函数,而且没法写出解析表达式,因此我们假设该函数的输出与输入在一个二维窗口内满足线性关系:
q
i
=
a
k
I
i
+
b
k
,
∀
i
∈
w
k
q_i = a_k I_i + b_k, \forall i \in w_k
qi=akIi+bk,∀i∈wk
其中,q 是输出像素的值,I 是输入图像的值,i 和 k 是像素索引,a 和 b 是当窗口中心位于 k 时该线性函数的系数。
其实,输入图像不一定是待滤波的图像本身,也可以是其它图像即引导图像,这也是为何称为引导滤波的原因。对上式两边取梯度,可以得到:
▽
q
=
a
▽
I
\triangledown q = a \triangledown I
▽q=a▽I
即当输入图像 I 有梯度时,输出 q 也有类似的梯度,现在可以解释为什么引导滤波有边缘保持特性了。下一步是求出线性函数的系数,也就是线性回归,即希望拟合函数的输出值与真实值 p 之间的差距最小,也就是让下式最小:
E
(
a
k
,
b
k
)
=
∑
i
∈
w
k
(
(
a
k
I
i
+
b
k
−
p
i
)
2
+
ε
a
k
2
)
E(a_k, b_k) = \sum_{i \in w_k} ((a_kI_i + b_k - p_i)^2 + \varepsilon a_k^2)
E(ak,bk)=i∈wk∑((akIi+bk−pi)2+εak2)
这里 p 只能是待滤波图像,并不像 I 那样可以是其它图像。同时,a 之前的系数(以后都写为 e)用于防止求得的 a 过大,也是调节滤波器滤波效果的重要参数。通过最小二乘法,我们可以得到:
a
k
=
1
∣
w
∣
∑
i
∈
w
k
I
i
p
i
−
μ
k
p
ˉ
k
σ
k
2
+
ε
b
k
=
p
ˉ
k
−
a
k
μ
k
μ
k
是
I
在窗口
w
k
中的平均值,
σ
k
2
是
I
在窗口
w
k
中的方差,
∣
w
∣
是窗口
w
k
中像素的数量,
p
ˉ
k
是待滤波图像
p
在窗口
w
k
中的均值。
a_k = \frac{\frac{1}{|w|}\sum_{i \in w_k} I_i p_i - \mu_k \bar{p}_k}{\sigma_k^2 + \varepsilon} \\ b_k = \bar{p}_k - a_k \mu_k \\ \mu_k \text{ 是 } I \text{ 在窗口 } w_k \text{ 中的平均值,} \\ \sigma_k^2 \text{ 是 } I \text{ 在窗口 } w_k \text{ 中的方差,} \\ |w| \text{ 是窗口 } w_k \text{ 中像素的数量,} \\ \bar{p}_k \text{ 是待滤波图像 } p \text{ 在窗口 } w_k \text{ 中的均值。}
ak=σk2+ε∣w∣1∑i∈wkIipi−μkpˉkbk=pˉk−akμkμk 是 I 在窗口 wk 中的平均值,σk2 是 I 在窗口 wk 中的方差,∣w∣ 是窗口 wk 中像素的数量,pˉk 是待滤波图像 p 在窗口 wk 中的均值。
在计算每个窗口的线性系数时,我们可以发现一个像素会被多个窗口包含,也就是说,每个像素都由多个线性函数所描述。因此,如之前所说,要具体求某一点的输出值时,只需将所有包含该点的线性函数值平均即可:
q
i
=
1
∣
w
∣
∑
k
:
i
∈
w
k
(
a
k
I
i
+
b
k
)
=
a
ˉ
i
I
i
+
b
ˉ
i
w
k
是所有包含像素
i
的窗口,
k
是其中心位置。
q_i = \frac{1}{|w|}\sum_{k:i \in w_k} (a_k I_i + b_k) = \bar{a}_i I_i + \bar{b}_i\\ w_k是所有包含像素i的窗口,k是其中心位置。
qi=∣w∣1k:i∈wk∑(akIi+bk)=aˉiIi+bˉiwk是所有包含像素i的窗口,k是其中心位置。
当把引导滤波用作边缘保持滤波器时,往往有 I = p ,如果 e=0,显然 a=1, b=0 是 E(a,b) 为最小值的解,从上式可以看出,这时的滤波器没有任何作用,将输入原封不动的输出。
如果 e>0,在像素强度变化小的区域(或单色区域),有 a 近似于(或等于)0,而 b 近似于(或等于)0,即做了一个加权均值滤波; 而在变化大的区域,a 近似于1,b 近似于0,对图像的滤波效果很弱,有助于保持边缘。
而 e 的作用就是界定什么是变化大,什么是变化小。在窗口大小不变的情况下,随着 e 的增大,滤波效果越明显。
在滤波效果上,引导滤波和双边滤波差不多,在一些细节上,引导滤波较好。 引导滤波最大的优势在于,可以写出时间复杂度与窗口大小无关的算法,因此在使用大窗口处理图片时,其效率更高。
四、图像增强
有目的地强调图像的整体或局部特性,将原来不清晰的图像变得清晰或强调某些感兴趣的特征,扩大图像中不同物体特征之间的差别,抑制不感兴趣的特征,使之改善图像质量、丰富信息量,加强图像判读和识别效果,满足某些特殊分析的需要。
图像增强可以分为两种:
- 点处理技术。只对单个像素进行处理。
- 领域处理技术。对像素点及其周围的点进行处理,即使用卷积核。
1、点处理
1、线性变换
图像增强线性变换主要对图像的对比度和亮度进行调整:
y
=
a
∗
x
+
b
y=a*x+b
y=a∗x+b
参数 a 影响图像的对比度,参数 b 影响图像的亮度,具体可分为以下几种情况:
a>1:增强图像的对比度,图像看起来更加清晰。
a<1:减小了图像的对比度, 图像看起来变模糊。
a=1 and b≠0:图像整体的灰度值上移或者下移,也就是图像整体变亮或者变暗,不会改变图像的对比度,b>0 时图像变亮,b<0 时图像变暗。
2、分段线性变换
即对处于某个感兴趣的区域的 x,将其对比度系数 a 增大或减小,从而增大或减小这个区域的对比度。
{
y
=
a
1
∗
x
+
b
x
<
x
1
y
=
a
2
∗
x
+
b
x
1
<
x
<
x
2
y
=
a
1
∗
x
+
b
x
2
<
x
\begin{cases} y = a_1*x + b \quad x < x_1 \\ y = a_2*x + b \quad x_1 < x < x_2\\ y = a_1*x + b \quad x_2 < x \\ \end{cases}
⎩
⎨
⎧y=a1∗x+bx<x1y=a2∗x+bx1<x<x2y=a1∗x+bx2<x
3、对数变换
对数变换将图像的低灰度值部分扩展,将其高灰度值部分压缩,以达到强调图像低灰度部分的目的。
同时可以很好的压缩像素值变化较大的图像的动态范围,目的是突出我们需要的细节。
y
=
c
∗
l
o
g
(
1
+
x
)
y=c*log(1+x)
y=c∗log(1+x)
4、幂律变换/伽马变换
幂律变换主要用于图像的校正,对漂白的图片或者是过黑的图片进行修正。
y
=
c
∗
x
γ
y=c*x^γ
y=c∗xγ
根据 γ 的大小,主要可分为以下两种情况:
γ > 1:处理漂白的图片,进行灰度级压缩。
γ < 1:处理过黑的图片,对比度增强,使得细节看的更加清楚。
2、领域处理
直方图均衡化
图像滤波
3、图像增强常用方法
包括但不限于:
1、翻转、平移、旋转、缩放
2、分离单个 r、g、b 三个颜色通道
3、添加噪声
4、直方图均衡化
5、Gamma变换
6、反转图像的灰度
7、增加图像的对比度
8、缩放图像的灰度
9、均值滤波
10、中值滤波
11、高斯滤波
记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~