一、图像形态学
是一种处理图像形状特征的图像处理技术,主要用于描述和处理图像中的形状和结构。形态学可以用于提取图像中的特征、消除噪声、改变图像的形状等。
1.1 图像腐蚀
通俗来说腐蚀会让亮区域 "变小",暗区域 "扩大"
作用:去除小的亮噪点、分离粘连物体、细化前景轮廓
python
cv2.erode(src, kernel, anchor, iterations, borderType, borderValue)
常用参数介绍:
src:
传入需要腐蚀的图像数据
kernel:
腐蚀用的结构元素(核)
iterations:
腐蚀的迭代次数(默认1次),次数越多,腐蚀越强烈(过度腐蚀会丢失需要分析的目标区域)
python
import numpy as np
import cv2
sun = cv2.imread('../data/sun.png')
cv2.imshow('sun',sun)
cv2.waitKey(0)
kernel = np.ones((3,3),np.uint8)
erosion_1 = cv2.erode(sun,kernel,iterations = 2)
cv2.imshow('erosion_1',erosion_1)
cv2.waitKey(0)

1.2 图像膨胀
**"扩张" 或 "填充" 图像中的亮区域(前景)**,通俗来讲就像给前景的边缘 "涂厚一层",常用来填补前景的小缺口、连接断裂的线条、增强前景区域。
python
dst = cv2.dilate(src, kernel, anchor, iterations, borderType, borderValue)
常用参数介绍:
src:
传入需要腐蚀的图像数据
kernel:
膨胀用的结构元素(核)
iterations:
膨胀的迭代次数(默认1次),次数越多,膨胀越强烈(过度膨胀会导致需要分析的目标区域 "溢出")
python
import numpy as np
import cv2
wenzi = cv2.imread('../data/wenzi.png')
cv2.imshow('yuantu_wenzi',wenzi)
cv2.waitKey(0)
kernel = np.ones((2,2),np.uint8)
wenzi_new = cv2.dilate(wenzi,kernel,iterations = 2)
cv2.imshow('wenzi_new',wenzi_new)
cv2.waitKey(0)

cv2.morphologyEx()参数介绍:
python
cv2.morphologyEx(src, op, kernel, anchor, iterations, borderType, borderValue)
参数:
src:
输入图像,形态学操作的处理对象(多通道时会逐通道处理)
op:
指定要执行的形态学操作,可选参数:
cv2.MORPH_OPEN:开运算,先腐蚀 → 后膨胀,平滑物体的轮廓、断开较窄的狭颈并消除细的突出物
cv2.MORPH_CLOSE:闭运算,先膨胀 → 后腐蚀,弥合较窄的间断和细长的沟壑,消除小的孔洞,填补轮廓线中的断裂
cv2.MORPH_GRADIENT:梯度运算,膨胀结果 - 腐蚀结果(减去),提取物体边缘、轮廓
cv2.MORPH_TOPHAT:顶帽,原图像 - 开运算结果,突出原图像中比周围亮的小区域
cv2.MORPH_BLACKHAT:黑帽,闭运算结果 - 原图像,突出原图像中比周围暗的小区域
kernel:
选取结构元素(核)
anchor:
结构元素的锚点位置,默认中心即可,默认 (-1,-1)
iterations:
形态学操作的**迭代次数,**次数越多,效果越明显,默认为1
borderType:
图像边界填充方式
borderValue:
边界填充值(仅 borderType=cv2.BORDER_CONSTANT 时生效)
1.3 开运算(先腐蚀 → 后膨胀,平滑物体的轮廓、断开较窄的狭颈并消除细的突出物)
指定cv2.morphologyEx()的op参数为:cv2.MORPH_OPEN
python
import cv2
import pandas as pd
zhiwen = cv2.imread('../data/zhiwen.png')
cv2.imshow('yuantu_zhiwen',zhiwen)
cv2.waitKey(0)
kernel = np.ones((2,2),np.uint8)
zhiwen_new = cv2.morphologyEx(zhiwen,cv2.MORPH_OPEN,kernel)
cv2.imshow('zhiwen_new',zhiwen_new)
cv2.waitKey(0)

1.4 闭运算(先膨胀 → 后腐蚀,弥合较窄的间断和细长的沟壑,消除小的孔洞,填补轮廓线中的断裂)
指定cv2.morphologyEx()的op参数为:cv2.MORPH_CLOSE
python
import cv2
import pandas as pd
zhiwen_duan = cv2.imread('../data/zhiwen_duan.png')
cv2.imshow('yuantu_wenzi_duan',zhiwen_duan)
cv2.waitKey(0)
kernel = np.ones((4,4),np.uint8)
zhiwen__duan_new = cv2.morphologyEx(zhiwen_duan,cv2.MORPH_CLOSE,kernel)
cv2.imshow('zhiwen_duan__new',zhiwen__duan_new)
cv2.waitKey(0)

1.4 梯度运算(膨胀结果 - 腐蚀结果(减去),提取物体边缘、轮廓)
指定cv2.morphologyEx()的op参数为: cv2.MORPH_GRADIENT
python
import cv2
import pandas as pd
wenzi = cv2.imread('../data/wenzi.png')
cv2.imshow('yuantu_wenzi',wenzi)
cv2.waitKey(0)
kernel = np.ones((2,2),np.uint8)
#膨胀
pz_wenzi = cv2.dilate(wenzi,kernel,iterations = 1)
cv2.imshow('pz_wenzi',pz_wenzi)
cv2.waitKey(0)
#腐蚀
fs_wenzi = cv2.erode(wenzi,kernel,iterations = 1)
cv2.imshow('fs_wenzi',fs_wenzi)
cv2.waitKey(0)
#膨胀-腐蚀
bianyuan = cv2.morphologyEx(wenzi,cv2.MORPH_GRADIENT,kernel)
cv2.imshow('bianyuan',bianyuan)
cv2.waitKey(0)
cv2.destroyAllWindows()

1.5 顶帽(原图像 - 开运算结果,突出原图像中比周围亮的小区域)
指定cv2.morphologyEx()的op参数为: cv2.MORPH_TOPHAT
python
import pandas as pd
import cv2
sun = cv2.imread('../data/sun.png')
cv2.imshow('sun_yuantu',sun)
cv2.waitKey(0)
kernel = np.ones((2,2),np.uint8)
#开运算
open_sun = cv2.morphologyEx(sun,cv2.MORPH_OPEN,kernel)
cv2.imshow('open_sun',open_sun)
cv2.waitKey(0)
#顶帽
tophat = cv2.morphologyEx(sun,cv2.MORPH_TOPHAT,kernel)
cv2.imshow( 'TOPHAT',tophat)
cv2.waitKey(0)

1.6 黑帽(闭运算结果 - 原图像,突出原图像中比周围暗的小区域)
指定cv2.morphologyEx()的op参数为:cv2.MORPH_BLACKHAT
python
import cv2
import pandas as pd
sun = cv2.imread('../data/sun.png')
cv2.imshow('sun_yuantu',sun)
cv2.waitKey(0)
kernel = np.ones((2,2),np.uint8)
#闭运算
close_sun=cv2.morphologyEx(sun,cv2.MORPH_CLOSE,kernel)
cv2.imshow( 'close_sun',close_sun)
cv2.waitKey(0)
#黑帽
blackhat = cv2.morphologyEx(sun,cv2.MORPH_BLACKHAT,kernel)
cv2.imshow('BLACKHAT',blackhat)
cv2.waitKey(0)

二、边缘检测
旨在检测一张数字图像中有明显变化的边缘或者不连续的区域
2.1 sobel算子
计算图像在水平 / 垂直方向的像素变化(梯度)来识别边缘
python
cv2.Sobel(src, ddepth, dx, dy, ksize, scale, delta, borderType)
参数:
src:
输入的图像,梯度计算的处理对象,多通道会逐通道计算,
ddepth:
输出图像的深度(精度)
梯度计算会产生正负值 (比如边缘一侧像素值升高,一侧降低),而图像像素值范围是 [0,255](8 位无符号 CV_8U),直接用 CV_8U 会导致负数值被截断,丢失一半边缘
dx:
x 方向的导数阶数(0 = 不计算 x 方向,1 = 一阶导数,2 = 二阶导数,常用 1)
dy:
y 方向的导数阶数0 = 不计算 y 方向,1 = 一阶导数,2 = 二阶导数,常用 1)
ksize:(int,奇数)
Sobel 核的尺寸(核越大,检测的边缘越粗,噪声抑制越好)
delta:(float,默认 0)
加到输出结果上的偏移值,用于调整像素亮度
borderType:
图像边界填充方式
python
import cv2
import pandas as pd
yuan = cv2.imread('../data/yuan.png')
cv2.imshow('yuan', yuan)
cv2.waitKey(0)
#x方向上的边缘
yuan_x = cv2.Sobel(yuan, -1,dx=1,dy=0)
cv2.imshow('yuan_x',yuan_x)
cv2.waitKey(0)
#x方向上的边缘,包括负数信息(右端),但显示不出来,因为范围是(0~255)
yuan_x_64 = cv2.Sobel(yuan,cv2.CV_64F,dx=1,dy=0)#默认uint8改为float64,可保存负数
cv2.imshow( 'yuan_x_64',yuan_x_64)
cv2.waitKey(0)
#x方向上的边缘,包括负数信息,进行取绝对值的操作,右端的负值信息就可以显示出来了
yuan_x_full = cv2.convertScaleAbs(yuan_x_64)#转换为绝对值,负数转换为正数
cv2.imshow('yuan_x_full',yuan_x_full)
cv2.waitKey(0)
# y方向上的边缘
yuan_y = cv2.Sobel(yuan,-1,dx=0,dy=1)
cv2.imshow( 'yuan_y',yuan_y)
cv2.waitKey(0)
#y方向上的边缘,包括负数信息(下端),但显示不出来,因为范围是(0~255)
yuan_y_64 = cv2.Sobel(yuan,cv2.CV_64F, dx=0,dy=1)#默认int8改为float64, 可保存负数
yuan_y_full=cv2.convertScaleAbs (yuan_y_64)#转换为绝对值,负数转换为正数
cv2.imshow( 'yuan_y_full',yuan_y_full)
cv2.waitKey(0)
#如果同时使用x,y方向的结果如何呢?(不建议使用!!)
yuan_xy = cv2.Sobel(yuan, -1,dx=1,dy=1)
cv2.imshow( 'yuan_xy',yuan_xy)
cv2.waitKey(0)
#使用图像加权运算组合x和y方向的2个边缘。
yuan_xy_full = cv2.addWeighted(yuan_x_full,1,yuan_y_full,1,0)
cv2.imshow('yuan_xy_full',yuan_xy_full)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.2 Scharr算子
Scharr 算子是 Soble 算子在 ksize=3 时的优化,与 Soble 的速度相同,且精度更高。Scharr 算子与 Sobel 算子的不同点是在平滑部分,其中心元素占的权重更重,相当于使用较小标准差的高斯函数,也就是更瘦高的模板。
python
cv.Scharr(src, ddepth, dx, dy, scale, delta, borderType)
参数:
src:
输入图像,梯度计算的处理对象,多通道会逐通道计算。
ddepth:
输出图片的数据深度,由输入图像的深度进行选择
dx:
x 方向的导数阶数(仅支持 0 或 1,Scharr 不支持二阶导数)
dy:
y 方向的导数阶数(仅支持 0 或 1,且 dx+dy=1,不能同时为 0/1)
scale:(默认1)
梯度结果的缩放因子
delta:(默认0)
加到输出结果的偏移值,调整像素亮度
borderType:
图像边界填充方式
python
import cv2
import pandas as pd
amy = cv2.imread( '../data/img.png', cv2.IMREAD_GRAYSCALE)
amy_x_64 = cv2.Scharr (amy,cv2.CV_64F,dx=1,dy=0)#默认int8改为float64,可保存负数
amy_x_full = cv2.convertScaleAbs(amy_x_64)#转换为绝对值,负数转换为正数
amy_y_64 = cv2.Scharr (amy,cv2.CV_64F,dx=0,dy=1)#默认int8改为float64, 可保存负数
amy_y_full = cv2.convertScaleAbs(amy_y_64)#转换为绝对值,负数转换为正数
amy_xy_Scharr_full = cv2.addWeighted(amy_x_full, 1,amy_y_full, 1, 0)
cv2.imshow( 'amy_xy_Scharr_full',amy_xy_Scharr_full)
cv2.waitKey(0)


2.3 Laplacian算子
不再以x和y的方向计算,而是以圆方向计算变化率。因此不需要Gx+Gy。
python
dst = cv2.Scharr(src, ddepth, dx, dy, scale, delta, borderType)
src:
输入图像,梯度计算的处理对象,多通道会逐通道计算。
ddepth:
输出图片的数据深度,由输入图像的深度进行选择
dx:
x 方向的导数阶数(仅支持 0 或 1,Scharr 不支持二阶导数)
dy:
y 方向的导数阶数(仅支持 0 或 1,且 dx+dy=1,不能同时为 0/1)
scale:(默认1)
梯度结果的缩放因子
delta:(默认0)
加到输出结果的偏移值,调整像素亮度
borderType:
图像边界填充方式
python
import cv2
import pandas as pd
amy = cv2.imread( '../data/img.png', cv2.IMREAD_GRAYSCALE)
amy_lap = cv2.Laplacian(amy,cv2.CV_64F,ksize=3)
amy_lap_full=cv2.convertScaleAbs (amy_lap)#转换为绝对值,负数转换为正数
cv2.imshow('amy_lap_full',amy_lap_full)
cv2.waitKey(0)


2.4 Canny边缘检测
canny边缘检测分为4个部分:
1、图像降噪
图像去噪是进行边缘检测的第一步,通过去噪可以去除图像中的一些噪点,从而使边缘检测时免受噪点干扰。
2、梯度计算
要进行边缘检测,就需要得到图像梯度信息,根据图像的梯度幅值和梯度方向来确定边缘,一般均采用sobel算子对图像进行梯度幅值与梯度方向计算。
3、非极大值抑制
一阶微分在灰度值斜坡过渡时不为零且存在较粗的边缘,较粗的边缘会增大边缘检测的误差,因此需要细化边缘,一种较为常用的方法是非极大值抑制
非极大值抑制:即在梯度图像中寻找梯度方向上的最大值作为边缘,不是梯度方向上的最大值则抑制为0。因为梯度方向是灰度变化最大的方向。比较梯度图像中每一点的灰度值与梯度方向上至少两个梯度图像像素点灰度值的大小,根据上述大小关系来确定是否保留该点的灰度值。
4、双阈值边界跟踪
双阈值处理就是根据实际情况需要设置一个灰度高阈值和一个灰度低阈值对NMS后的图像进行过滤,使得得到的边缘尽可能是真实的边缘
双阈值的基本处理步骤为:
f>fH,将当前像素点标记为强边缘像素,保留像素点
fL<f<fH,将当前像素点标记为弱边缘像素,若像素点所在的边缘存在强边缘像素,则保留,否则置0
f<fL,将当前像素点灰度置零。
python
edges = cv2.Canny(image, threshold1, threshold2, apertureSize, L2gradient)
参数:
image:
输入的图像,推荐先转**灰度图,**多通道会自动转灰度
threshold1:
低阈值(fL),弱边缘判定下限
threshold2:
高阈值(fH),强边缘判定上限(需满足 threshold2 > threshold1)
apertureSize;
Sobel/Scharr 核的尺寸,用于计算梯度
L2gradient:(布尔值)
梯度幅值计算方式:False → 用 L1 范数,计算快。
True → 用 L2 范数,精度更高。
python
import cv2
import pandas as pd
amy = cv2.imread( '../data/img.png', cv2.IMREAD_GRAYSCALE)
cv2.imshow('amy',amy)
cv2.waitKey(0)
amy_canny = cv2.Canny(amy,100,150)
cv2.imshow('amy_canny',amy_canny)
cv2.waitKey(0)


Canny边缘检测的优点:
低错误率。因为一般的边缘检测算子可能存在检测到伪边缘的情况,因此Canny算法检测到的边缘尽可能地是真实的边缘。
较好地定位边缘点。由检测器标记的边缘点与真实边缘点中心尽可能地接近。
单一的边缘响应。图像中的边缘只标记出一次。