1.颜色空间/颜色模型
1.1 RGB模型
RGB(Red, Green, Blue)模型表示的图像由3个分量图像组成,每种原色一幅分量图像。当送入RGB监视器时,这3幅图像在屏幕上混合生成一幅合成的彩色图像。考虑一幅RGB图像,其中每一幅图红绿蓝图像都是一幅8比特图像。在这种情况下,可以说每个RGB彩色像素有24比特的深度。在24比特RGB图像中,颜色总数是(28)3=16777216。下图为分别为RGB彩色立方体示意图和对应的RGB24比特彩色立方体。
RGB颜色空间最常用的用途就是显示器系统,彩色阴极射线管,彩色光栅图形的显示器 都使用R、G、B数值来驱动R、G、B 电子枪发射电子,并分别激发荧光屏上的R、G、B三种颜色的荧光粉 发出不同亮度的光线,并通过相加混合产生各种颜色;扫描仪也是通过吸收原稿经反射或透射而发送来 的光线中的R、G、B成分,并用它来表示原稿的颜色。
1.2 CMY/CMYK 模型
CMYK(Cyan, Magenta,Yellow, blacK)颜色空间多应用于印刷工业,印刷业通过青©、品(M)、黄(Y)三原色油墨的不同 网点面积率的叠印来表现丰富多彩的颜色和阶调,这便是三原色的CMY颜色空间。大多数在纸上沉积彩色颜料的设备,如彩色打印机和复印机,要求输入CMY数据或在内部进行RGB到CMY的转换。这一转换是使用下面的式子进行的:
[CMY]=[111]−[RGB]\left[\begin{array}{l}
C \\
M \\
Y
\end{array}\right]=\left[\begin{array}{l}
1 \\
1 \\
1
\end{array}\right]-\left[\begin{array}{l}
R \\
G \\
B
\end{array}\right]CMY=111−RGB
1.3 HSI 模型
HSI (Hue, Saturation, Intensity )模型有时候也写作HIS、HLS模型,是从人的视觉系统出发,用色调(Hue)、色饱和度(Saturation)和亮度 (Intensity)来描述色彩。其中,色调是描述纯色(纯黄色、纯橙色或纯红色)的颜色属性。饱和度是一种纯色被白光稀释的程度的度量。亮度是一个主观描述子,体现无色的强度概念。
HSI色彩空间可以用一个圆锥空间模型来描述,如下图所示。色彩空间的圆锥模型相当复杂,但确能把色调、亮度和色饱和度的变化情形表现得很清楚。在HSI色彩空间可以大大简化图像分析 和处理的工作量。
HSI色彩空间和RGB色彩空间只是同一物理量的不同表示法,因而它们之间存在着转换关系。给定一幅RGB彩色图像,每个RGB像素的H分量,S分量和I分量计算方式如下:
H={arccos{(R−G)+(R−B)2(R−G)2+(R−B)(G−B)}B≤G2π−arccos{(R−G)+(R−B)2(R−G)2+(R−B)(G−B)}B>GS=1−3(R+G+B)min(R,G,B)I=(R+G+B)3\begin{array}{l}
H=\left\{\begin{array}{cc}
\arccos \left\{\frac{(R-G)+(R-B)}{2 \sqrt{(R-G)^{2}+(R-B)(G-B)}}\right\} & B \leq G \\
2 \pi-\arccos \left\{\frac{(R-G)+(R-B)}{2 \sqrt{(R-G)^{2}+(R-B)(G-B)}}\right\} & B>G
\end{array}\right. \\
S=1-\frac{3}{(R+G+B)} \min (R, G, B) \\
I=\frac{(R+G+B)}{3} \\
\end{array}H=⎩⎨⎧arccos{2(R−G)2+(R−B)(G−B)(R−G)+(R−B)}2π−arccos{2(R−G)2+(R−B)(G−B)(R−G)+(R−B)}B≤GB>GS=1−(R+G+B)3min(R,G,B)I=3(R+G+B)
1.4 HSV 模型
HSV(Hue, Saturation, Value)模型比HSI模型更与人类对颜色的感知接近。H代表色调,S代表饱和度,V代表亮度值。HSV模型的坐标系统可以是圆柱坐标系统,但一般用六棱锥来表示,如下图所示,与HSI模型比较相似。
HSV和RGB之间也存在转换关系,转换公式为:
H={arccos{(R−G)+(R−B)2(R−G)2+(R−B)(G−B)}B≤G2π−arccos{(R−G)+(R−B)2(R−G)2+(R−B)(G−B)}B>GS=max(R,G,B)−min(R,G,B)max(R,G,B)V=max(R,G,B)255\begin{array}{l}
H=\left\{\begin{array}{cc}
\arccos \left\{\frac{(R-G)+(R-B)}{2 \sqrt{(R-G)^{2}+(R-B)(G-B)}}\right\} & B \leq G \\
2 \pi-\arccos \left\{\frac{(R-G)+(R-B)}{2 \sqrt{(R-G)^{2}+(R-B)(G-B)}}\right\} & B>G
\end{array}\right. \\
S=\frac{max(R,G,B)-min(R,G,B)}{max(R,G,B)} \\
V=\frac{max(R,G,B)}{255} \\
\end{array}H=⎩⎨⎧arccos{2(R−G)2+(R−B)(G−B)(R−G)+(R−B)}2π−arccos{2(R−G)2+(R−B)(G−B)(R−G)+(R−B)}B≤GB>GS=max(R,G,B)max(R,G,B)−min(R,G,B)V=255max(R,G,B)
1.5 HSB 模型
HSB(Hue, Saturation, Brightness)模型的基础是对立色理论,对立色理论源于人们对对立色调(红和绿、黄和蓝)的观察事实(对立色调的颜色叠加,它们会相互抵消)。HSB模型是普及型设计软件中常见的色彩模式,其中H代表色相;S代表饱和度;B代表亮度。HSB模型的如下图所示:
-
色调H(Hue):在0~360°的标准色环上,按照角度值标识。比如红是0°、橙色是30°等。
-
饱和度S( Saturation ):是指颜色的强度或纯度。饱和度表示色相中彩色成分所占的比例,用从0%(灰色)~100%(完全饱和)的百分比来度量。在色立面上饱和度是从左向右逐渐增加的,左边线为0%,右边线为100%。
-
亮度B( Brightness ):是颜色的明暗程度,通常是从0(黑)~100%(白)的百分比来度量的,在色立面中从上至下逐渐递减,上边线为100%,下边线为0% 。
1.6 Lab 模型
**Lab(Commission International EclairageLab)**是一种不常用的色彩空间。它是在1931年国际照明委员会(Commission International Eclairage, CIE)制定的颜色度量国际标准的基础上建立起来的。1976年,经修改后被正式命名为CIELab。它是用数字化的方法来描述人的视觉感应。Lab颜色空间中的L分量用于表示像素的亮度,取值范围是[0,100],表示从纯黑到纯白;a表示从红色到绿色的范围,取值范围是[127,-128];b表示从黄色到蓝色的范围,取值范围是[127,-128]。下图所示为Lab颜色空间的图示:
1.7 YUV 模型
YUV是一种颜色编码方法。常使用在各个视频处理组件中。 YUV在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。YUV是编译true-color颜色空间(color space)的种类,Y’UV, YUV, YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠。“Y”表示明亮度(Luminance、Luma),“U”和“V”则是色度、浓度(Chrominance、Chroma)。
在现代彩色电视系统中,通常采用三管彩色摄像机或彩色CCD(点耦合器件)摄像机,它把摄得的彩色图像 信号,经分色、分别放大校正得到RGB,再经过矩阵变换电路得到亮度信号Y和两个色差信号R-Y、B-Y, 最后发送端将亮度和色差三个信号分别进行编码,用同一信道发送出去。这就是我们常用的YUV色彩空间。
根据美国国家电视制式委员会,NTSC制式的标准,当白光的 亮度用Y来表示时,它和红、绿、蓝三色光的关系可用如下式的方程描述:Y=0.3R+0.59G+0.11B 这就是常用 的亮度公式。色差U、V是由B-Y、R-Y按不同比例压缩而成的。如果要由YUV空间转化成RGB空间,只要进行 相反的逆运算即可。
RGB与YUV的转换公式如下:
[YUV]=[0.2990.5870.114−0.148−0.2890.4370.615−0.515−0.100][RGB]\left[\begin{array}{l}
Y \\
U \\
V
\end{array}\right]=\left[\begin{array}{l}
0.299\quad\quad0.587\quad\quad0.114 \\
-0.148\quad-0.289\quad0.437 \\
0.615\quad-0.515\quad-0.100
\end{array}\right]\left[\begin{array}{l}
R \\
G \\
B
\end{array}\right]YUV=0.2990.5870.114−0.148−0.2890.4370.615−0.515−0.100RGB
1.8 代码示例
#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
#读取原始图像
img_BGR = cv2.imread('cat.jpg')
#BGR转换为RGB
img_RGB = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2RGB)
#灰度化处理
img_GRAY = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2GRAY)
#BGR转HSV
img_HSV = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2HSV)
#BGR转YCrCb
img_YCrCb = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2YCrCb)
#BGR转HLS
img_HLS = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2HLS)
#BGR转XYZ
img_XYZ = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2XYZ)
#BGR转LAB
img_LAB = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2LAB)
#BGR转YUV
img_YUV = cv2.cvtColor(img_BGR, cv2.COLOR_BGR2YUV)
#调用matplotlib显示处理结果
titles = ['BGR', 'RGB', 'GRAY', 'HSV', 'YCrCb', 'HLS', 'XYZ', 'LAB', 'YUV']
images = [img_BGR, img_RGB, img_GRAY, img_HSV, img_YCrCb,
img_HLS, img_XYZ, img_LAB, img_YUV]
for i in range(9):
plt.subplot(3, 3, i+1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
输出结果如下所示:
2.图像阈值化处理
图像的 二值化或阈值化 (Binarization) 旨在提取图像中的目标物体,将背景以及噪声区分开来。通常会设定一个阈值 𝑇,通过阈值𝑇将图像的像素划分为两类:大于阈值𝑇的像素群和小于阈值𝑇的像素群。
灰度转换处理后的图像中,每个像素都只有一个灰度值,其大小表示明暗程度。二值化处理可以将图像中的像素划分为两类颜色,常用的二值化算法如下所示:
{Y=0,gray<TY=255,gray≥T\left\{\begin{array}{cc}
Y=0, & gray < T \\
Y=255, & gray \geq T
\end{array}\right.{Y=0,Y=255,gray<Tgray≥T其中,当灰度Gray小于阈值T时,其像素设置为0,表示黑色;当灰度Gray大于或等于阈值T时,其Y值为255,表示白色。
2.1 二进制阈值化
二进制阈值化方法先要选定一个特定的阈值量,比如127。新的阈值产生规则如下:
dst(x,y)={maxVal,ifsrc(x,y)>thresh0,otherwisedst(x,y)=\left\{\begin{array}{cc}
maxVal, & if src(x,y)>thresh \\
0, & otherwise
\end{array}\right.dst(x,y)={maxVal,0,ifsrc(x,y)>threshotherwise
(1) 大于等于127的像素点的灰度值设定为最大值(如8位灰度值最大为255);
(2) 灰度值小于127的像素点的灰度值设定为0 ;
例如,163->255,86->0,102->0,201->255
代码示例如下:
#encoding:utf-8
import cv2
import numpy as np
#读取图片
src = cv2.imread("cat.jpg", cv2.IMREAD_UNCHANGED)
#灰度图像处理
GrayImage = cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)
#二进制阈值化处理
r, b = cv2.threshold(GrayImage, 127, 255, cv2.THRESH_BINARY)
print (r)
# 显示原始图像
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(src, cv2.COLOR_BGR2RGB))
plt.title('Original')
plt.axis('off')
# 显示二值化图像
plt.subplot(1, 2, 2)
plt.imshow(b, cmap='gray')
plt.title('Thresholded')
plt.axis('off')
plt.show()
2.2 反二进制阈值化
反二进制阈值化方法与二进制阈值化方法相似,先要选定一个特定的灰度值作为阈值,比如127。但新的阈值产生规则如下公式所示:
dst(x,y)={0,ifsrc(x,y)>threshmaxVal,otherwisedst(x,y)=\left\{\begin{array}{cc}
0, & if src(x,y)>thresh \\
maxVal, & otherwise
\end{array}\right.dst(x,y)={0,maxVal,ifsrc(x,y)>threshotherwise
(1) 小于127的像素点的灰度值设定为最大值(如8位灰度值最大为255);
(2) 灰度值大于127的像素点的灰度值设定为0 ;
例如,163->0,86->255,102->255,201->0
代码示例如下:
#encoding:utf-8
import cv2
import numpy as np
#读取图片
src = cv2.imread("cat.jpg", cv2.IMREAD_UNCHANGED)
#灰度图像处理
GrayImage = cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)
#反二进制阈值化处理
r, b = cv2.threshold(GrayImage, 127, 255, cv2.THRESH_BINARY_INV)
print (r)
# 显示原始图像
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(src, cv2.COLOR_BGR2RGB))
plt.title('Original')
plt.axis('off')
# 显示二值化图像
plt.subplot(1, 2, 2)
plt.imshow(b, cmap='gray')
plt.title('Thresholded_Inv')
plt.axis('off')
plt.show()
显示结果如下:
2.3 截断阈值化
截断阈值化方法需要选定一个阈值,图像中大于该阈值的像素点被设定为该阈值,小于该阈值的保持不变,比如127。新的阈值产生规则如下:
dst(x,y)={threshold,ifsrc(x,y)>threshsrc(x,y),otherwisedst(x,y)=\left\{\begin{array}{cc}
threshold, & if src(x,y)>thresh \\
src(x,y), & otherwise
\end{array}\right.dst(x,y)={threshold,src(x,y),ifsrc(x,y)>threshotherwise
(1) 大于等于127的像素点的灰度值设定为该阈值127;
(2) 小于该阈值的灰度值不改变;
例如,163->127,86->86,102->102,201->127。
代码示例如下:
#encoding:utf-8
import cv2
import numpy as np
#读取图片
src = cv2.imread("cat.jpg", cv2.IMREAD_UNCHANGED)
#灰度图像处理
GrayImage = cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)
#截断阈值化处理
r, b = cv2.threshold(GrayImage, 127, 255, cv2.THRESH_TRUNC)
print (r)
# 显示原始图像
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(src, cv2.COLOR_BGR2RGB))
plt.title('Original')
plt.axis('off')
# 显示二值化图像
plt.subplot(1, 2, 2)
plt.imshow(b, cmap='gray')
plt.title('Thresholded_Trunc')
plt.axis('off')
plt.show()
显示结果如下:
2.4 反阈值化为0
反阈值化为0 方法先选定一个阈值,比如127,接着对图像的灰度值进行如下处理:
dst(x,y)={0,ifsrc(x,y)>threshsrc(x,y),otherwisedst(x,y)=\left\{\begin{array}{cc}
0, & if src(x,y)>thresh \\
src(x,y), & otherwise
\end{array}\right.dst(x,y)={0,src(x,y),ifsrc(x,y)>threshotherwise
(1) 大于等于阈值127的像素点变为0 ;
(2) 小于该阈值的像素点值保持不变;
例如,163->0,86->86,102->102,201->0。
代码示例如下:
#encoding:utf-8
import cv2
import numpy as np
#读取图片
src = cv2.imread("cat.jpg", cv2.IMREAD_UNCHANGED)
#灰度图像处理
GrayImage = cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)
#反阈值化为0处理
r, b = cv2.threshold(GrayImage, 127, 255, cv2.THRESH_TRUNC)
print (r)
# 显示原始图像
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(src, cv2.COLOR_BGR2RGB))
plt.title('Original')
plt.axis('off')
# 显示二值化图像
plt.subplot(1, 2, 2)
plt.imshow(b, cmap='gray')
plt.title('plt.title('Thresholded_Tozero_Inv')')
plt.axis('off')
plt.show()
显示结果如下:
2.5 阈值化为0
阈值化为0方法先选定一个阈值,比如127,接着对图像的灰度值进行如下处理:
dst(x,y)={src(x,y),ifsrc(x,y)>thresh0,otherwisedst(x,y)=\left\{\begin{array}{cc}
src(x,y), & if src(x,y)>thresh \\
0, & otherwise
\end{array}\right.dst(x,y)={src(x,y),0,ifsrc(x,y)>threshotherwise
(1) 大于等于阈值127的像素点,值保持不变;
(2) 小于该阈值的像素点值设置为0 ;
例如,163->163,86->0,102->0,201->201。
代码示例如下:
#encoding:utf-8
import cv2
import numpy as np
#读取图片
src = cv2.imread("cat.jpg", cv2.IMREAD_UNCHANGED)
#灰度图像处理
GrayImage = cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)
#阈值化为0处理
r, b = cv2.threshold(GrayImage, 127, 255, cv2.THRESH_TOZERO)
print (r)
# 显示原始图像
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(src, cv2.COLOR_BGR2RGB))
plt.title('Original')
plt.axis('off')
# 显示二值化图像
plt.subplot(1, 2, 2)
plt.imshow(b, cmap='gray')
plt.title('plt.title('Thresholded_Tozero')')
plt.axis('off')
plt.show()
显示结果如下:
将所有结果统一显示可见:
3.图像平滑处理
图像平滑是一种区域增强的算法,平滑算法有邻域平均法、中指滤波、边界保持类滤波等。在图像产生、传输和复制过程中,常常会因为多方面原因而被噪声干扰或出现数据丢失,降低了图像的质量(某一像素,如果它与周围像素点相比有明显的不同,则该点被噪声所感染)。这就需要对图像进行一定的增强处理以减小这些缺陷带来的影响。
图像平滑有均值滤波、方框滤波、中值滤波和高斯滤波等。
3.1 均值滤波
均值滤波是典型的线性滤波算法,是指用当前像素点周围NxN个像素值的均值来代替当前像素值。使用该方法遍历处理图像内的每一个像素点,可完成整幅图像的均值滤波。例如下图中,红色点的像素值是其周围蓝色背景区域像素值之和除25,25=5×5 是蓝色区域的大小。
其中5×5的矩阵称为核,针对原始图像内的像素点,采用核进行处理,得到结果图像,如下图所示:
提取 1/25 可以将核转换为如下形式:
如果N的值越大,参与运算的值就越大,参与运算的像素点的数量就越多,图像失真就越严重。
分别使用3x3、5x5、10x10的核对图片进行均值滤波平滑,代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('8206.jpg')
source = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 均值滤波
result_3x3 = cv2.blur(source, (3, 3))
result_5x5 = cv2.blur(source, (5, 5))
result_10x10 = cv2.blur(source, (10, 10))
# 显示图形
titles = ['Source Image', 'Blur Image (3x3)', 'Blur Image (5x5)', 'Blur Image (10x10)']
images = [source, result_3x3, result_5x5, result_10x10]
plt.figure(figsize=(12, 6))
for i in range(4):
plt.subplot(1, 4, i + 1)
plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([])
plt.yticks([])
plt.show()
3.2 中值滤波
在使用邻域平均法去噪的同时也使得边界变得模糊。而中值滤波是非线性的图像处理方法,在去噪的同时可以兼顾到边界信息的保留。选一个含有奇数点的窗口W,将这个窗口在图像上扫描,把窗口中所含的像素点按灰度级的升或降序排列,取位于中间的灰度值来代替该点的灰度值。计算过程如下图所示:
需要注意的是:核必须是大于1的奇数,如3、5、7等。
分别使用3x3、5x5、7x7的核对图片进行中值滤波平滑,代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('8206.jpg')
# 中值滤波
result_3x3 = cv2.medianBlur(img, 3)
result_5x5 = cv2.medianBlur(img, 5)
result_7x7 = cv2.medianBlur(img, 7)
# 显示图形
titles = ['Source Image', 'medianBlur Image (3x3)', 'medianBlur Image (5x5)', 'medianBlur Image (7x7)']
images = [img, result_3x3, result_5x5, result_7x7]
plt.figure(figsize=(15, 5))
for i in range(len(titles)):
plt.subplot(1, 4, i+1)
plt.imshow(cv2.cvtColor(images[i], cv2.COLOR_BGR2RGB))
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
3.3 高斯滤波
为了克服简单局部平均法的弊端(图像模糊),目前已提出许多保持边缘、细节的局部平滑算法。高斯平滑就是邻域平均的思想对图像进行平滑的一种,在图像高斯平滑中,对图像进行平均时,不同位置的像素被赋予了不同的权重。高斯平滑与简单平滑不同,它在对邻域内像素进行平均时,给予不同位置的像素不同的权值。
通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波让临近的像素具有更高的重要度,对周围像素计算加权平均值,较近的像素具有较大的权重值。
需要注意的是:核必须是大于1的奇数,如3、5、7等。
分别使用3x3、5x5、7x7的核对图片进行高斯滤波平滑,代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('8206.png')
source = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 均值滤波
result_3x3 = cv2.GaussianBlur(source, (3, 3), 0)
result_5x5 = cv2.GaussianBlur(source, (3, 3), 0)
result_7x7 = cv2.GaussianBlur(source, (7, 7), 0)
#第三个参数sigmaX 是必选参数,但是可以将该参数设置为 0,让函数自己去计算 sigmaX 的具体值
# 显示图形
titles = ['Source Image', 'GaussianBlur Image (3x3)', 'GaussianBlur Image (5x5)', 'GaussianBlur Image (7x7)']
images = [source, result_3x3, result_5x5, result_7x7]
plt.figure(figsize=(12, 6))
for i in range(4):
plt.subplot(1, 4, i + 1)
plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([])
plt.yticks([])
plt.show()
4.形态学处理
形态学(morphology) 一词通常表示生物学的一个分支,该分支主要研究动植物的形态和结构。这里,我们使用同一词语表示数学形态学的内容,将数学形态学作为工具从图像中提取表达和描绘区域形状的有用图像分量,如边界、骨架和凸壳等。
形态学处理主要针对的是二值图像(0或1)。
4.1 图像腐蚀与图像膨胀
膨胀与腐蚀能够实现以下作用:
- 消除噪声
- 分割出独立的图像元素,在图像中连接相邻的元素
- 寻找图像中的明显的极大值区域或者极小值区域
- 求出图像的梯度
从数学的角度来说,膨胀和腐蚀操作就是将图像与核进行卷积,核可以是任意形状和大小的。核大,则周围对其影响大,变化大,核小,则周围对其影响小,变化小。需要注意之处: 腐蚀和膨胀都是对图像的白色部分(高亮部分)而言。
4.1.1 图像腐蚀
腐蚀就是求局部最小值的操作。核B与图像卷积,即计算核B覆盖的区域的像素点的最小值,并把这个最小值赋值给参考点指定的像素。这样就会使图像中高亮区域逐渐减少。
把二值图像各1像素连接成分的边界点去掉从而缩小一层(可提取骨干信息,去掉毛刺,去掉孤立的0像素);是原图的高亮部分被腐蚀,类似于领域被蚕食,效果图拥有比原图更小的高亮区域。
腐蚀的运算符是“-”,其定义如下:
A−B={x∣Bx⊆A}A-B=\left\{ x|B_x \subseteq A \right\} A−B={x∣Bx⊆A}
表示图像A用卷积模板B来进行腐蚀处理,通过模板B与图像A进行卷积计算,得出B覆盖 区域的像素点最小值,并用这个最小值来替代参考点的像素值。
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('1.png')
# 二值化处理
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 设置卷积核
kernel_5x5 = np.ones((5, 5), np.uint8)
kernel_10x10 = np.ones((10, 10), np.uint8)
kernel_25x25 = np.ones((25, 25), np.uint8)
# 图像腐蚀处理
erosion_5x5 = cv2.erode(img, kernel_5x5)
erosion_5x5_1 = cv2.erode(img, kernel_5x5, iterations=5)
erosion_10x10 = cv2.erode(img, kernel_10x10)
# 显示图形
titles = ['Source Image', 'Erosion (5x5)', 'Erosion (5x5)1', 'Erosion (10x10)']
images = [img, erosion_5x5, erosion_5x5_1,erosion_10x10,]
plt.figure(figsize=(15, 5))
for i in range(len(titles)):
plt.subplot(1, 4, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
4.1.2 图像膨胀
图像膨胀是腐蚀操作的逆操作,类似于“领域扩张”,将图像中的高亮区域或白色部分进行扩张,其运行结果图比原图的高亮区域更大,线条变粗了,主要用于去噪。膨胀就是求局部最大值的操作。核B与图像卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。这样就会使图像中高亮区域逐渐增长。
膨胀的运算符是“⊕”,其定义如下:
A⊕B={x∣Bx∩A≠Θ}A\oplus B = \left\{x|B_x \cap A \ne \Theta \right\}A⊕B={x∣Bx∩A=Θ} 该公式表示用B来对图像A进行膨胀处理,其中B是一个卷积模板或卷积核,其形状可以为正方形或圆形,通过模板B与图像A进行卷积计算,得出B覆盖区域的像素点最大值,并用该值替换参考点的像素值实现膨胀。
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('1.png')
# 设置卷积核
kernel_5x5 = np.ones((5, 5), np.uint8)
kernel_10x10 = np.ones((10, 10), np.uint8)
kernel_25x25 = np.ones((25, 25), np.uint8)
# 图像腐蚀处理
erosion_5x5 = cv2.dilate(img, kernel_5x5)
erosion_5x5_1 = cv2.dilate(img, kernel_5x5, iterations=5)
erosion_10x10 = cv2.dilate(img, kernel_10x10)
# 显示图形
titles = ['Source Image', 'Erosion (5x5)', 'Erosion (5x5)1', 'Erosion (10x10)']
images = [img, erosion_5x5, erosion_5x5_1,erosion_10x10,]
plt.figure(figsize=(15, 5))
for i in range(len(titles)):
plt.subplot(1, 4, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
4.2 开运算与闭运算
4.2.1 开运算
图像开运算是图像依次经过先腐蚀、后膨胀处理后的过程。图像被腐蚀后,去除了噪声,但是也压缩了图像;接着对腐蚀过的图像进行膨胀处理,可以去除噪声,并保留原有图像。可以把看上去细微连在一起的两个目标分开了。可以有效去除背景噪声。
开运算的优势有:
- 开运算能够除去孤立的小点,毛刺和小桥,而总的位置和形状不变。
- 开运算是一个基于几何运算的滤波器。
- 结构元素大小的不同将导致滤波效果的不同。
- 不同的结构元素的选择导致了不同的分割,即提取出不同的特征。
以下为开运算原理图解:
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('2.png')
# 设置卷积核
kernel_5x5 = np.ones((5, 5), np.uint8)
kernel_7x7= np.ones((25, 25), np.uint8)
#图像开运算
result1 = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel_5x5)
result2 = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel_5x5, iterations=3)
result3 = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel_7x7)
# 显示图形
titles = ['Source Image', 'Erosion (5x5)', 'Erosion (5x5)1', 'Erosion (7x7)']
images = [img, result1, result2, result3]
plt.figure(figsize=(15, 5))
for i in range(len(titles)):
plt.subplot(1, 4, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
4.2.2 闭运算
图像闭运算是图像依次经过先膨胀、后腐蚀处理后的过程。图像先膨胀,后腐蚀,它有助于关闭前景物体内部的小孔,或物体上的小黑点。闭运算能够填平小湖(即小孔),弥合小裂缝,而总的位置和形状不变。可以有效去除前景噪声。
闭运算的优势有:
- 闭运算能够填平小湖(即小孔),弥合小裂缝,而总的位置和形状不变。
- 闭运算是通过填充图像的凹角来滤波图像的。
- 结构元素大小的不同将导致滤波效果的不同。
- 不同结构元素的选择导致了不同的分割。
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('3.png')
# 设置卷积核
kernel_5x5 = np.ones((5, 5), np.uint8)
kernel_7x7= np.ones((25, 25), np.uint8)
#图像开运算
result1 = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel_5x5)
result2 = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel_5x5, iterations=3)
result3 = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel_7x7)
# 显示图形
titles = ['Source Image', 'Erosion (5x5)', 'Erosion (5x5)1', 'Erosion (7x7)']
images = [img, result1, result2, result3]
plt.figure(figsize=(15, 5))
for i in range(len(titles)):
plt.subplot(1, 4, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
4.3 图像梯度运算
图像梯度运算通过计算图像膨胀结果减去腐蚀结果的差值,得到图像的边界信息。具体来说,膨胀操作是将图像中的高亮区域或白色部分进行扩张,而腐蚀操作则是将图像中的高亮区域缩小。因此,通过计算膨胀减去腐蚀的差值,可以得到图像中物体的边缘轮廓,即图像的梯度信息。这种操作可以用于提取图像中的边缘特征,增强图像的可视化效果,以及进行图像分割和识别等任务。 其中二值图像1表示白色点,0表示黑色点。
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('1.png')
# 设置卷积核
kernel_5x5 = np.ones((5, 5), np.uint8)
kernel_7x7= np.ones((25, 25), np.uint8)
#图像开运算
result1 = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel_5x5)
result2 = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel_5x5, iterations=3)
result3 = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel_7x7)
# 显示图形
titles = ['Source Image', 'Erosion (5x5)', 'Erosion (5x5)1', 'Erosion (7x7)']
images = [img, result1, result2, result3]
plt.figure(figsize=(15, 5))
for i in range(len(titles)):
plt.subplot(1, 4, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
4.4 顶帽运算与黑帽运算
4.4.1 顶帽运算
图像顶帽(或图像礼帽)运算是原始图像减去图像开运算的结果,用来得到图像的背景噪声。
因为开运算到来的结果是放大了裂痕或者局部低亮度的区域,因此,从原图中减去运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('2.png')
# 设置卷积核
kernel_5x5 = np.ones((5, 5), np.uint8)
kernel_7x7= np.ones((25, 25), np.uint8)
#图像开运算
result1 = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel_5x5)
result2 = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel_5x5, iterations=3)
result3 = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel_7x7)
# 显示图形
titles = ['Source Image', 'Erosion (5x5)', 'Erosion (5x5)1', 'Erosion (7x7)']
images = [img, result1, result2, result3]
plt.figure(figsize=(15, 5))
for i in range(len(titles)):
plt.subplot(1, 4, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
4.4.2 黑帽运算
黑帽运算是图像闭运算操作减去原始图像的结果,得到图像内部的小孔,或者前景色中的小黑点。用来得到图像的前景噪声。
黑帽运算之后的效果图突出了与原图像轮廓周围的区域更暗的区域,且这一操作和选择的核大小相关。所以黑帽运算用来分离比邻近点暗一些的斑块。
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('3.png')
# 设置卷积核
kernel_5x5 = np.ones((5, 5), np.uint8)
kernel_7x7= np.ones((25, 25), np.uint8)
#图像开运算
result1 = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel_5x5)
result2 = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel_5x5, iterations=3)
result3 = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel_7x7)
# 显示图形
titles = ['Source Image', 'Erosion (5x5)', 'Erosion (5x5)1', 'Erosion (7x7)']
images = [img, result1, result2, result3]
plt.figure(figsize=(15, 5))
for i in range(len(titles)):
plt.subplot(1, 4, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
5.图像灰度变换
灰度变换是图像增强的一种重要手段,用于改善图像显示效果,属于空间域处理方法,它可以使图像动态范围加大,使图像对比度扩展,图像更加清晰,特征更加明显。灰度变换其实质就是按一定的规则修改图像每一个像素的灰度,从而改变图像的灰度范围。常见的灰度变换包括线性灰度变换和非线性灰度变换。
5.1 线性灰度变换
灰度线性变换最常见的就是图像反转,在灰度图像灰度级范围[0,L−1][0,L−1][0,L−1]中,其反转的公式如下所示:
s=L−1−rs=L-1-rs=L−1−r
其中,r表示原始图像的灰度级,s表示变换后的灰度级。
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('1.png')
#图像灰度转换
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#获取图像高度和宽度
height = grayImage.shape[0]
width = grayImage.shape[1]
#创建一幅图像
result = np.zeros((height, width), np.uint8)
#图像灰度反色变换 s=255-r
for i in range(height):
for j in range(width):
gray = 255 - grayImage[i,j]
result[i,j] = np.uint8(gray)
# 显示图形
titles = ['Source Image', 'Gray Image']
images = [img, result]
plt.figure(figsize=(15, 5))
for i in range(len(titles)):
plt.subplot(1, 2, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
5.2 非线性灰度变换
5.2.1 对数变换
灰度的线性变换不利于突出感兴趣区域,且容易出现饱和、截止等情况。有时原图含有较多的暗部区域,某些显示设备呈现低灰度的能力有限,如果直接使用原图,则一部分细节可能丢失。进行非线性变换,即对原图进行对数变换,增强暗部区域的对比度。
灰度对数变换一般表示如下所示:
s=clog(1+r)s=clog(1+r)s=clog(1+r)其中,r表示原始图像的灰度级,s表示变换后的灰度级,c为常数,表示尺度比例系数,用来调节变换后的灰度值的动态范围。
对数变换可以扩展图像的低灰度值范围,同时压缩高灰度值范围,使图像灰度值分布均匀,更加匹配人的视觉特性。对数变换尤其适用于灰度范围特别大的情况。
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('8206.png')
#对数变换
def log(c, img):
output = c * np.log(1.0 + img)
output = np.uint8(output + 0.5)
return output
#图像灰度对数变换
output = log(42, img)
# 显示图形
titles = ['Source Image', 'Gray Image']
images = [img, output]
plt.figure(figsize=(15, 5))
for i in range(len(titles)):
plt.subplot(1, 2, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
5.2.2 伽马变换
伽玛变换又称为 指数变换 或 幂次变换,是另一种常用的灰度非线性变换。灰度的伽玛变换一般表示如下所示:
s=crγs=cr^{\gamma}s=crγ其中,r表示原始图像的灰度级,s表示变换后的灰度级,c和γ为正常数。
当γ < 1 \gamma<1γ<1 时,低亮度区域对比度增强
当γ > 1 \gamma>1γ>1 时, 高亮度区域对比度增强
当γ = 1 \gamma=1γ=1时,该灰度变换是线性的,此时通过线性方式改变原图像。
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图片
img = cv2.imread('8206.png')
#噶玛变换
def gamma(img, c, v):
lut = np.zeros(256, dtype=np.float32)
for i in range(256):
lut[i] = c * i ** v
output_img = cv2.LUT(img, lut) #像素灰度值的映射
output_img = np.uint8(output_img+0.5)
return output_img
#图像灰度对数变换
output = gamma(img, 0.00000005, 4.0)
# 显示图形
titles = ['Source Image', 'Gray Image']
images = [img, output]
plt.figure(figsize=(15, 5))
for i in range(len(titles)):
plt.subplot(1, 2, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
6.边缘检测
6.1 基本原理
图像边缘是图像最基本的特征,所谓边缘(Edge) 是指图像局部特性的不连续性。灰度或结构等信息的突变处称之为边缘。例如,灰度级的突变、颜色的突变,、纹理结构的突变等。边缘是一个区域的结束,也是另一个区域的开始,利用该特征可以分割图像。
在一维空间中,类似的操作被称作步长检测(step detection)。边缘是一幅图像中不同屈原之间的边界线,通常一个边缘图像是一个二值图像。边缘检测的目的是捕捉亮度急剧变化的区域。
图像的边缘有方向和幅度两种属性。边缘通常可以通过一阶导数或二阶导数检测得到。一阶导数是以最大值作为对应的边缘的位置,而二阶导数则以过零点作为对应边缘的位置。
6.2 常见边缘检测算子
6.2.1 一阶导数的边缘算子
6.2.1.1 Roberts算子
Roberts算子又称为交叉微分算法,它是基于交叉差分的梯度算法,通过局部差分计算检测边缘线条。常用来处理具有陡峭的低噪声图像,当图像边缘接近于正45度或负45度时,该算法处理效果更理想。其缺点是对边缘的定位不太准确,提取的边缘线条较粗。
Roberts算子的模板分为水平方向和垂直方向,如下式所示,从其模板可以看出,Roberts算子能较好的增强正负45度的图像边缘。
dx=[−1001]dy=[0−110]d_x=\left[ \begin{matrix} -1 & 0 \\ 0 & 1 \\ \end{matrix} \right]\begin{matrix} {} & {} \\ {} & {} \\ \end{matrix}{{d}_{y}}=\left[ \begin{matrix} 0 & -1 \\ 1 & 0 \\ \end{matrix} \right]dx=[−1001]dy=[01−10]
示例代码如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread('8206.png')
img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #转成RGB 方便后面显示
# 灰度化处理图像
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Roberts算子
kernelx = np.array([[-1, 0], [0, 1]], dtype=int)
kernely = np.array([[0, -1], [1, 0]], dtype=int)
x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
# 转uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 显示图形
plt.subplot(121),plt.imshow(img_RGB),plt.title('原始图像'), plt.axis('off') #坐标轴关闭
plt.subplot(122),plt.imshow(Roberts, cmap=plt.cm.gray ),plt.title('Roberts算子'), plt.axis('off')
plt.show()
6.2.1.2 Prewitt算子
Prewitt算子是一种图像边缘检测的微分算子,其原理是利用特定区域内像素灰度值产生的差分实现边缘检测。由于Prewitt算子采用 3(\times)3 模板对区域内的像素值进行计算,而Robert算子的模板为 2(\times)2,故Prewitt算子的边缘检测结果在水平方向和垂直方向均比Robert算子更加明显。Prewitt算子适合用来识别噪声较多、灰度渐变的图像,其计算公式如下所示:
dy=[−101−101−101]dx=[−1−1−1000111]d_y=\left[ \begin{matrix} -1 & 0 & 1 \\ -1 & 0 & 1 \\ -1 & 0 & 1 \\ \end{matrix} \right]\begin{matrix} {} & {} & {} \\ {} & {} & {} \\ {} & {} & {} \\ \end{matrix}{{d}_{x}}=\left[ \begin{matrix} -1 & -1 & -1 \\ 0 & 0 & 0 \\ 1 & 1 & 1 \\ \end{matrix} \right]dy=−1−1−1000111dx=−101−101−101
示例代码如下:
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread('8206.png')
img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #转成RGB 方便后面显示
# 灰度化处理图像
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Prewitt算子
kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
# 转uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Prewitt = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 显示图形
plt.subplot(121),plt.imshow(img_RGB),plt.title('原始图像'), plt.axis('off') #坐标轴关闭
plt.subplot(122),plt.imshow(Prewitt, cmap=plt.cm.gray ),plt.title('Prewitt算子'), plt.axis('off')
plt.show()
6.2.1.3 Sobel算子
Sobel算子是一种用于边缘检测的离散微分算子,它结合了高斯平滑和微分求导。该算子用于计算图像明暗程度近似值,根据图像边缘旁边明暗程度把该区域内超过某个数的特定点记为边缘。Sobel算子在Prewitt算子的基础上增加了权重的概念,认为相邻点的距离远近对当前像素点的影响是不同的,距离越近的像素点对应当前像素的影响越大, 从而实现图像锐化并突出边缘轮廓。
Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息。因为Sobel算子结合了高斯平滑和微分求导(分化),因此结果会具有更多的抗噪性,当对精度要求不是很高时,Sobel算子是一种较为常用的边缘检测方法。
Sobel算子的边缘定位更准确,常用于噪声较多、灰度渐变的图像。其算法模板如下面的公式所示,其中 ({{d}{x}}) 表示水平方向,({{d}{y}}) 表示垂直方向。
dx=[−101−202−101]dy=[−1−2−1000121]d_x=\left[ \begin{matrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \\ \end{matrix} \right]\begin{matrix} {} & {} & {} \\ {} & {} & {} \\ {} & {} & {} \\ \end{matrix}{{d}_{y}}=\left[ \begin{matrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1 \\ \end{matrix} \right]dx=−1−2−1000121dy=−101−202−101
示例代码如下:
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread('1111.png')
img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #转成RGB 方便后面显示
# 灰度化处理图像
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Sobel算子
x = cv2.Sobel(grayImage, cv2.CV_16S, 1, 0) # 对x求一阶导
y = cv2.Sobel(grayImage, cv2.CV_16S, 0, 1) # 对y求一阶导
# 转uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 显示图形
plt.subplot(121),plt.imshow(img_RGB),plt.title('原始图像'), plt.axis('off') #坐标轴关闭
plt.subplot(122),plt.imshow(Sobel, cmap=plt.cm.gray ),plt.title('Sobel算子'), plt.axis('off')
plt.show()
6.2.2 二阶导数的边缘算子(Laplacian 算子)
常见的二阶导数的边缘算子主要是Laplacian 算子。拉普拉斯(Laplacian) 算子是 (n) 维欧几里德空间中的一个二阶微分算子,常用于图像增强领域和边缘提取。它通过灰度差分计算邻域内的像素。其计算公式为:
△src=∂2src∂x2+∂2src∂y2\bigtriangleup src=\frac{\partial^{2} src }{\partial x^{2} } +\frac{\partial^{2} src }{\partial y^{2} }△src=∂x2∂2src+∂y2∂2src
即 x 方向和 y 方向的二阶微分之和。直观理解,标准的Laplace算子所做的事情其实就是将一个3x3邻域内中心点上下左右的值之和,减去中心值。因为它使用的是二阶导数,Laplacian滤波器对图像中的突变(即边缘)更加敏感。
Laplacian 算子的基本流程是:
- 判断图像中心像素灰度值与它周围其他像素的灰度值,如果中心像素的灰度更高,则提升中心像素的灰度;反之降低中心像素的灰度,从而实现图像锐化操作;
- 在算法实现过程中,Laplacian算子通过对邻域中心像素的四方向或八方向求梯度,再将梯度相加起来判断中心像素灰度与邻域内其他像素灰度的关系;
- 最后通过梯度运算的结果对像素灰度进行调整。
Laplacian算子分为四邻域和八邻域,四邻域是对邻域中心像素的四个方向求梯度,八邻域是对八个方向求梯度。
其中,Laplacian算子四邻域模板如下所示:
H=[0−10−14−10−10]H=\left[ \begin{matrix} 0 & -1 & 0 \\ -1 & 4 & -1 \\ 0 & -1 & 0 \\ \end{matrix} \right]H=0−10−14−10−10
Laplacian算子的八邻域模板如下所示:
H=[−1−1−1−18−1−1−1−1]H=\left[ \begin{matrix} -1 & -1 & -1 \\ -1 & 8 & -1 \\ -1 & -1 & -1 \\ \end{matrix} \right]H=−1−1−1−18−1−1−1−1
通过Laplacian算子的模板可以发现:
1)当邻域内像素灰度相同时,模板的卷积运算结果为0;
2)当中心像素灰度高于邻域内其他像素的平均灰度时,模板的卷积运算结果为正数;
3)当中心像素的灰度低于邻域内其他像素的平均灰度时,模板的卷积为负数。对卷积运算的结果用适当的衰弱因子处理并加在原中心像素上,就可以实现图像的锐化处理。
示例代码如下:
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread('1111.png')
img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #转成RGB 方便后面显示
# 灰度化处理图像
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 拉普拉斯算法
dst = cv2.Laplacian(grayImage, cv2.CV_16S, ksize=3)
Laplacian = cv2.convertScaleAbs(dst)
# 转uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Laplacian = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 显示图形
plt.subplot(121),plt.imshow(img_RGB),plt.title('原始图像'), plt.axis('off') #坐标轴关闭
plt.subplot(122),plt.imshow(Laplacian, cmap=plt.cm.gray ),plt.title('Laplacian算子'), plt.axis('off')
plt.show()
6.2.3 其他边缘算子(Canny 算子)
Canny算法是一种被广泛应用于边缘检测的标准算法,其目标是找到一个最优的边缘检测解或找寻一幅图像中灰度强度变化最强的位置。最优边缘检测主要通过低错误率、高定位性和最小响应三个标准进行评价。Canny算子的简要步骤如下:
-
应用高斯滤波去除图像噪声
由于图像边缘非常容易受到噪声的干扰,因此为了避免检测到错误的边缘信息,通常需要对图像进行滤波以去除噪声。滤波的目的是平滑一些纹理较弱的非边缘区域,以便得到更准确的边缘。在实际处理过程中,通常采用高斯滤波去除图像中的噪声。
在滤波过程中,我们通过滤波器对像素点周围的像素计算加权平均值,获取最终滤波结果。滤波器的大小也是可变的,高斯核的大小对于边缘检测的效果具有很重要的作用。滤波器
的核越大,边缘信息对于噪声的敏感度就越低。不过,核越大,边缘检测的定位错误也会随之增加。通常来说,一个 5×5 的核能够满足大多数的情况。 -
计算梯度的幅度与方向
梯度的方向与边缘的方向是垂直的。梯度的方向被归为四类:垂直、水平和两个对角线(即,0度、45度、90度和135度四个方向)。
Canny算子中按照Sobel算子计算梯度幅值和方向,寻找图像的梯度。先将卷积模板分别作用x和y方向,再计算梯度幅值S和方向θ,其公式如下所示:
({{d}{x}}=\left[ \begin{matrix} -1 & 0 & 1 \ -2 & 0 & 2 \ -1 & 0 & 1 \ \end{matrix} \right]\begin{matrix} {} & {} & {} \ {} & {} & {} \ {} & {} & {} \ \end{matrix}{{d}{y}}=\left[ \begin{matrix} -1 & -2 & -1 \ 0 & 0 & 0 \ 1 & 2 & 1 \ \end{matrix} \right])
S=dx2+dy2S=\sqrt{d_{x}^{2}+d_{y}^{2} } S=dx2+dy2θ=arctan(dydx)\theta =\arctan (\frac{d_{y} }{d_{x} } )θ=arctan(dxdy) -
非极大值抑制
对于每个像素点,它进行如下操作:应用非最大抑制技术来过滤掉非边缘像素,将模糊的边界变得清晰。该过程保留了每个像素点上梯度强度的极大值,过滤掉其他的值。
-
应用双阈值的方法来决定可能的(潜在的)边界
经过非极大抑制后图像中仍然有很多噪声点。Canny算法中应用了一种叫双阈值的技术。即设定一个阈值上界和阈值下界(opencv中通常由人为指定的),图像中的像素点如果大于阈值上界则认为必然是边界(称为强边界,strong edge),小于阈值下界则认为必然不是边界,两者之间的则认为是候选项(称为弱边界,weak edge),需进行进一步处理。过程如下图所示:
-
利用滞后技术来跟踪边界
若某一像素位置和强边界相连的弱边界认为是边界,其他的弱边界则被删除。
示例代码如下:
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread('1111.png')
img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #转成RGB 方便后面显示
# 灰度化处理图像
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 高斯滤波降噪
gaussian = cv2.GaussianBlur(grayImage, (5, 5), 0)
# Canny算子
Canny = cv2.Canny(gaussian, 50, 150)
# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 显示图形
plt.subplot(121),plt.imshow(img_RGB),plt.title('原始图像'), plt.axis('off') #坐标轴关闭
plt.subplot(122),plt.imshow(Canny, cmap=plt.cm.gray ),plt.title('Canny算子'), plt.axis('off')
plt.show()