加法混色
三通道:RGB
一个像素的颜色值:(b,g,r)
取值范围:[0,255] or [0.0,1.0]
人类视觉概念,画家配色
三要素
一个像素的颜色值:(h,s,v)
取值范围:[0,255] or [0.0,1.0]
国际照明协会1931年提出
基于人类颜色视觉的直接测定
其他颜色空间基础
人类视觉系统-视锥细胞
三色刺激值通道
(三通道彩色图片到单通道灰度图是单向变换)
直方图均衡(Histogram Equalization)是(图像处理)领域中利用直方图对对比度进行调整的方法.
顾名思义, 直方图均衡是将直方图的分布(概率密度)调整为均匀分布。
根据信息论,信息的熵越大,包含的信息也就越多,熵的计算公式如下:
H = − ∑ i = 0 n p ( x i ) l o g ( p ( x i ) ) H = -\sum_{i=0}^{n}p(x_i)log{(p(x_i))} H=−i=0∑np(xi)log(p(xi))
只有当$ p(x_i) $ 为均匀分布时,熵的值最大,对应到图像上,当直方图均匀分布时,图像对比度最大。如下图所示:
蓝色为原始图像直方图,绿色为均衡后直方图,对应的处理后图像为:
可以直观地看出,直方图均衡处理后,图像变得更加清晰了。
直方图均衡化通常用来增加许多图像的局部对比度,尤其是当图像的有用数据的对比度相当接近的时候。直方图均衡化以后,亮度可以更好地在直方图上分布。这样就可以用于增强局部的对比度而不影响整体的对比度,直方图均衡化通过有效的扩展常用的亮度来实现这种功能。
直方图均衡化在实质上是对图像进行非线性拉伸。重新分配各个灰度单位中的像素点数量,使一定灰度范围像素点数量的值大致相等。
对图片数据/特征分布的一种统计
区间(bin)
对数据空间(bin)进行量化
通常做直方图均值有以下几个步骤
p r ( r k ) = n k H ∗ W , k = 0 , 1 , 2 , . . . , L − 1 p_r(r_k)=\frac{n_k}{H*W} , k=0,1,2,...,L-1 pr(rk)=H∗Wnk,k=0,1,2,...,L−1
式中, H , W H,W H,W分别为图像的高和宽, n k n_k nk表示灰度值为 r k r_k rk的像素个数, s k s_k sk为变换后的灰度值, T ( r k ) T(r_k) T(rk)为映射函数,计算过程使用了累计直方图
设原始直方图分布为: p r ( r k ) p_r(r_k) pr(rk)
均衡化后的直方图分布为: p s ( s k ) = 1 L − 1 p_s(s_k)=\frac{1}{L-1} ps(sk)=L−11
映射函数为: s k = T ( r k ) s_k=T(r_k) sk=T(rk)
这里映射函数必须为单调递增函数,满足: ∫ 0 s k p s ( s ) d s = ∫ 0 r k p r ( r ) d r \int_{0}^{s_k}p_s(s)ds=\int_{0}^{r_k}p_r(r)dr ∫0skps(s)ds=∫0rkpr(r)dr
就是说对应区域间内像素点的总数总是一样的,如下图红色区域所示:
我们将 p s ( s k ) = 1 L − 1 p_s(s_k)=\frac{1}{L-1} ps(sk)=L−11代入 ∫ 0 s k p s ( s ) d s = ∫ 0 r k p r ( r ) d r \int_{0}^{s_k}p_s(s)ds=\int_{0}^{r_k}p_r(r)dr ∫0skps(s)ds=∫0rkpr(r)dr,则有:
s k = ( L − 1 ) ∫ 0 r k P r ( r ) d r s_k=(L-1)\int_{0}^{r_k}P_r(r)dr sk=(L−1)∫0rkPr(r)dr
与之所对应的离散形式的公式为 s k = T ( r k ) = ( L − 1 ) ∑ j = 0 k p r ( r j ) s_k=T(r_k)=(L-1)\sum_{j=0}^{k}p_r(r_j) sk=T(rk)=(L−1)∑j=0kpr(rj)
直方图均衡过度地强调了灰度个数的重要性,对数量多的灰度过度地进行了增强,而图像中,比例不多的灰度往往更重要,因此改进的方向就是对数量较多的灰度进行减少影响。可以如何改进呢?
我们可以对直方图进行截断,超出部分直接去除,从而减少灰度过多的带来的影响。那在此基础上,我们还可以将超出的部分均匀地加到直方图的每个bin上,拿着就是CLAHE了。
直方图均衡的经典算法对整幅图像的像素使用相同的变换,如果图像中包括明显亮的或是暗的区域,则经典算法作用有限。
自适应直方图均衡(AHE)算法通过对局部区域进行直方图均衡来解决上述问题。步骤如下:
AHE会过度放大图像中相对均匀区域的噪音,可采用限制对比度自适应直方图均衡即CLAHE,与普通的自适应直方图均衡相比,CLAHE的不同地方在于直方图修剪的过程,用修剪后的直方图均衡图像时,图像的对比度会更自然。
数学上定义:线性插值是指插值函数为一次多项式的插值方式,其在插值节点上的插值误差为0;在图片上,我们利用线性插值的算法,可以减少图片的锯齿,模糊。
单线性插值是在一个方向上进行线性插值,比如X方向;下面将根据维基百科说明如何进行线性插值:
假设我们已知坐标 ( x 0 , y 0 ) (x_0,y_0) (x0,y0)与 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),要得到 [ x 0 , x 1 ] [x_0,x_1] [x0,x1]区间内某一位置 x x x在直线上的值。根据图中所示,我们得到:
y − y 0 x − x 0 = y 1 − y 0 x 1 − x 0 \frac{y-y_0}{x-x_0}=\frac{y_1-y_0}{x_1-x_0} x−x0y−y0=x1−x0y1−y0
由于 x x x值已知,所以可以从公式得到 y y y的值:
y = y 0 + ( x − x 0 ) y 1 − y 0 x 1 − x 0 = y 0 + ( x − x 0 ) y 1 − ( x − x 0 ) y 0 x 1 − x 0 y = y_0 + (x-x_0)\frac{y_1-y_0}{x_1-x_0} = y_0 + \frac{(x-x_0)y_1-(x-x_0)y_0}{x_1-x_0} y=y0+(x−x0)x1−x0y1−y0=y0+x1−x0(x−x0)y1−(x−x0)y0
已知 y y y求 x x x的过程与以上过程相同,只是 x x x与 y y y要进行交换。
双线性插值是有两个变量的插值函数的单线性插值扩展,核心思想是在两个方向上分别进行一次线性插值。
f ( R 1 ) ≈ x 2 − x x 2 − x 1 f ( Q 11 ) + x − x 1 x 2 − x 1 f ( Q 21 ) w h e r e R 1 = ( x , y 1 ) f ( R 2 ) ≈ x 2 − x x 2 − x 1 f ( Q 12 ) + x − x 1 x 2 − x 1 f ( Q 22 ) w h e r e R 2 = ( x , y 2 ) W e p r o c e e d b y i n t e r p o l a t i n g i n t h e y − d i r e c t i o n . f ( P ) ≈ y 2 − y y 2 − y 1 f ( R 1 ) + y − y 1 y 2 − y 1 f ( R 2 ) f(R_1)\approx \frac{x_2-x}{x_2-x_1}f(Q_{11})+\frac{x-x_1}{x_2-x_1}f(Q_{21}) \qquad where \quad R_1 = (x,y_1)\\ \\ f(R_2)\approx \frac{x_2-x}{x_2-x_1}f(Q_{12})+\frac{x-x_1}{x_2-x_1}f(Q_{22}) \qquad where \quad R_2 = (x,y_2)\\ \\ We\quad proceed\quad by\quad interpolating\quad in\quad the\quad y-direction.\\ \\ f(P)\approx \frac{y_2-y}{y_2-y_1}f(R_1)+\frac{y-y_1}{y_2-y_1}f(R_2) f(R1)≈x2−x1x2−xf(Q11)+x2−x1x−x1f(Q21)whereR1=(x,y1)f(R2)≈x2−x1x2−xf(Q12)+x2−x1x−x1f(Q22)whereR2=(x,y2)Weproceedbyinterpolatinginthey−direction.f(P)≈y2−y1y2−yf(R1)+y2−y1y−y1f(R2)
结构元素:设有两幅图像A、S。若A是被处理的对象,而S是用来处理A的,则称S为结构元素,通常是比较小的图像,S必须具有原点。
腐蚀:就是让原本位于图像原点的结构元素S在整个 Z 2 Z^2 Z2平面上进行移动,当S的原点平移至某一点时(假定这个点为z),S可以完全包含在A中,则所有这样的点z构成的集合,即为S对A的腐蚀图像。
膨胀:让原本位于图像原点的结构元素S在 Z 2 Z^2 Z2上移动,当自身原点平移至z点,S和A有交集,也就是说至少有一个元素是重叠的,这样的z点构成的集合也就是S对A的膨胀图像。
开运算:先腐蚀再膨胀。
闭运算:先膨胀再腐蚀。
通常,当有噪声的图像用阈值二值化后,所得到的边界是很不平滑的,物体区域具有一些错判的孔洞,背景区域散步着一些小的噪声物体,连续的开和闭运算可以显著的改善这种情况。
开运算使图像的轮廓变得光滑,断开狭窄的连接,消除毛刺和孤立点。闭运算同样使得轮廓变得光滑,它通常能够弥合狭窄的间断,填充小的孔洞。
注意:所有的形态学运算都是针对图像中的前景物体进行的。大多数软件将物体用黑色表示(灰度值为0),背景用白色表示(灰度值为255),如C++就遵循此规定。但是Matlab在二值图像形态学处理中,默认白色为前景,而黑色为背景。
在这里我们使用如下图例进行演示:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('test.png')
def cv_show(img):
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
plt.show()
#腐蚀操作
kernel = np.ones((3,3),np.uint8)
test_erosion = cv2.erode(img,kernel,iterations = 1)
'''
cv2.erode(src,kernel,iterations)
src:输入图片
kernel:方框大小
iterations:迭代次数
'''
cv_show(test_erosion)
输出结果为:
#膨胀操作
kernel = np.ones((3,3),np.uint8)
test_dilate = cv2.dilate(img,kernel,iterations = 1)
'''
cv2.dilate(src,kernel,iterations)
src:输入图片
kernel:方框大小
iterations:迭代次数
'''
cv_show(test_dilate)
输出结果为:
#开运算:先腐蚀,再膨胀
kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)
'''
cv2.morphologhEx(src,op,kernel)
src:输入图片
op: cv2.MORPH_OPEN:开运算
cv2.MORPH_CLOSE:闭运算
cv2.MORPH_GRADIENT:形态学梯度
cv2.MORPH_TOPHAT:顶帽,突出比原轮廓亮的部分
cv2.MORPH_BLACKHAT:黑帽,突出比原轮廓暗的地方
kernel:方框大小,核大小,滤波器
'''
cv_show(opening)
输出结果:
#闭运算:先膨胀,再腐蚀
kernel = np.ones((5,5),np.uint8)
closing = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)
cv_show(closing)
接下来我们使用如下图例来演示形态学的梯度运算。
#梯度= 膨胀-腐蚀
pie = cv2.imread('test2.png')
kernel = np.ones((7,7),np.uint8)
dilate = cv2.dilate(pie,kernel,iterations=5)
erosion = cv2.erode(pie,kernel,iterations=5)
res = np.hstack((dilate,erosion))
cv_show(res)
我们先对比一下膨胀与腐蚀的结果:
gradient = cv2.morphologyEx(pie,cv2.MORPH_GRADIENT,kernel)
cv_show(gradient)
输出结果为:
滤波/卷积:在每个图片位置(x,y)上进行基于邻域的函数计算
h [ x , y ] = ∑ k , l f [ k , l ] I [ x + k , y + l ] h[x,y] = \sum_{k,l}f[k,l]I[x+k,y+l] h[x,y]=k,l∑f[k,l]I[x+k,y+l]
图像处理中滤波和卷积是常用到的操作。两者在原理上相似,但是在实现的细节上存在一些区别。
简单来说,滤波操作就是图像对应像素与掩膜(mask)的乘积之和。如有一张图片和一个掩膜如下所示:
那么像素 ( i , j ) (i,j) (i,j)的滤波后结果可以根据以下公式计算:
G ( i , j ) = I ( i − 1 , j − 1 ) × m 1 + I ( i , j − 1 ) × m 2 + I ( i + 1 , j − 1 ) × m 3 + I ( i − 1 , j ) × m 4 + I ( i , j ) × m 5 + I ( i + 1 , j ) × m 6 + I ( i − 1. j + 1 ) × m 7 + I ( i , j + 1 ) × m 8 + I ( i + 1 , j + 1 × m 9 G(i,j)=I(i-1,j-1)\times m1 + I(i,j-1)\times m2 + I(i+1,j-1)\times m3 +\\ I(i-1,j)\times m4 + I(i,j)\times m5+I(i+1,j)\times m6 + I(i-1.j+1)\times m7 \\+ I(i,j+1)\times m8 + I(i+1,j+1\times m9 G(i,j)=I(i−1,j−1)×m1+I(i,j−1)×m2+I(i+1,j−1)×m3+I(i−1,j)×m4+I(i,j)×m5+I(i+1,j)×m6+I(i−1.j+1)×m7+I(i,j+1)×m8+I(i+1,j+1×m9
其中 G ( i , j ) G(i,j) G(i,j)是图片中 ( i , j ) (i,j) (i,j)位置像素经过滤波后的像素值。当掩膜中心 m 5 m5 m5位置移动到图像 ( i , j ) (i,j) (i,j)像素位置时,图像 ( i , j ) (i,j) (i,j)位置像素成为锚点。
滤波的步骤:
按照这个步骤,假设我们有一个二维矩阵I,掩膜M,则滤波的结果如下:
卷积的原理与滤波类似。但是有一些细小的区别。
卷积操作也是卷积核与图像对应位置的乘积和。但是卷积操作在做乘积之前需要先将卷积核翻转180°,之后再做乘积。
在此可以看出如果卷积核不是中心对称的,那么卷积和滤波操作将会得到完全不一样的结果,另外卷积操作会改变图像大小(损失图像边缘),所以为了保证卷积后图像大小与原图一致,常用的做法是在卷积操作之前对图像进行边缘填充。
我们使用如下图例来进行演示:
import cv2
import numpy as np
import matplotlib.pyplot as plt
#定义上下左右四个方向的填充大小
top_size,bottom_size,left_size,right_size = (50,50,50,50)
#边界复制策略
replicate =cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,
borderType=cv2.BORDER_REPLICATE)
#镜像:对图像中的像素在两边进行复制如:fedcba|abcdefgh|hgfedcb
reflect = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,
cv2.BORDER_REFLECT)
#镜像:以最边缘像素为轴,如:gfedcb|abcdefgh|gfedcba
reflect101=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,
cv2.BORDER_REFLECT_101)
#块复制,如:cdefgh|abcdefgh|abcdefg
wrap = cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,
cv2.BORDER_WRAP)
#常量法,常数值填充
constant=cv2.copyMakeBorder(img,top_size,bottom_size,left_size,right_size,
cv2.BORDER_CONSTANT,value=0)
plt.subplot(231),plt.imshow(img,'gray'),plt.title('ORGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')
plt.show()
输出结果为: