原理
OpenCV中的边缘检测原理主要基于图像梯度的计算,包括一阶梯度和二阶梯度。
- 一阶梯度:它反映了图像亮度变化的速度。Sobel算法就是一种以一阶梯度为基础的边缘检测算法。它通过计算图像在水平和垂直方向上的梯度来检测边缘。这种方法简单有效,但对于噪声较为敏感。
- 二阶梯度:它描述了图像亮度变化率的变化,可以用于检测图像中的细线和角落。Laplacian算法就是基于二阶梯度的边缘检测算法,它对边缘的定位非常准确,但同样对噪声比较敏感。
边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。边缘的表现形式:
图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。边缘检测可以划分为两类:基于搜索和基于零穿越。
- 基于搜索:通过寻找图像一阶导数中的最大值来检测边界,然后利用计算结果估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值,代表算法是Sobel算子和Scharr算子。
- 基于零穿越:通过寻找图像二阶导数零穿越来寻找边界,代表算法是Laplacian算子。
Sobel检测算子
Sobel边缘检测算法比较简单,实际应用中效率比canny边缘检测效率要高,但是边缘不如Canny检测的准确,但是很多实际应用的场合,sobel边缘却是首选,Sobel算子是高斯平滑与微分操作的结合体,所以其抗噪声能力很强,用途较多。尤其是效率要求较高,而对细纹理不太关心的时候。
OpenCV中常用的边缘检测算法包括Sobel、Scharr、Laplacian和Canny等。
- Sobel算法:通过计算图像亮度的空间梯度来检测边缘,通常用于水平和垂直边缘的检测。
- Scharr算法:与Sobel类似,但使用了不同的核来计算梯度,通常能提供更精确的边缘检测结果。
- Laplacian算法:通过计算图像的二阶导数来检测边缘,对边缘的定位非常准确,但对噪声也比较敏感。
- Canny算法:是一个多阶段的边缘检测算法,包括使用高斯滤波器平滑图像、计算梯度幅度和方向、应用非最大抑制以及使用双阈值法进行边缘跟踪。Canny算法被认为是一种最优的边缘检测方法,因为它试图最小化误检和漏检的边缘。
API
利用OpenCV进行sobel边缘检测的API是cv2.Sobel()
函数。
Sobel_x_or_y = cv2.Sobel(src, ddepth, dx, dy, ksize)
-
src:传入的图像
-
ddepth: 图像的深度
-
dx和dy: 求导的阶数,0表示这个方向上没有求导,取值为0、1。
-
ksize: Sobel算子的大小,必须为奇数1、3、5、7,默认为3。
Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,所以Sobel建立的图像位数不够,会有截断。因此要使用16位有符号的数据类型,即cv2.CV_16S。处理完图像后,再使用cv2.convertScaleAbs()函数将其转回原来的uint8格式,否则图像无法显示。
Sobel算子是在两个方向计算的,最后还需要用cv2.addWeighted( )函数将其组合起来
Scale_abs = cv2.convertScaleAbs(x)
result = cv2.addWeighted(src1, alpha, src2, beta)
案例:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('/h.jpg',0)
x = cv.Sobel(img, cv.CV_16S, 1, 0)
y = cv.Sobel(img, cv.CV_16S, 0, 1)
Scale_absX = cv.convertScaleAbs(x)
Scale_absY = cv.convertScaleAbs(y)
result = cv.addWeighted(Scale_absX, 0.5, Scale_absY, 0.5, 0)
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(result,cmap = plt.cm.gray),plt.title('Sobel')
plt.xticks([]), plt.yticks([])
plt.show()
Laplacian算子
Laplacian算子,也称为拉普拉斯算子,是数学中的一个二阶微分算子,用于描述函数在邻域平均值与差的平均值之间的关系。
Laplacian算子在多个领域有着广泛的应用,包括但不限于:
- 数学领域:在数学中,Laplacian算子被用于定义在欧几里得空间中的二阶微分算子,它可以表示为梯度(grad)的散度(div)。这个算子可以用来描述保守场或有势场的性质,例如静电场或引力场。
- 物理领域:在物理学中,Laplacian算子经常出现在描述物理系统的偏微分方程中,如电磁学中的Poisson方程,它用来求解静电势分布问题。
此外,在图像处理中,Laplacian算子可以用于边缘检测,通过计算图像的二阶导数来检测图像中的边缘,这对于图像识别和计算机视觉任务非常重要。
API:
laplacian = cv2.Laplacian(src, ddepth, ksize)
- Src: 需要处理的图像,
- Ddepth: 图像的深度,-1表示采用的是原图像相同的深度,目标图像的深度必须大于等于原图像的深度;
- ksize:算子的大小,即卷积核的大小,必须为1,3,5,7。
案例:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('ho.jpg',0)
result = cv.Laplacian(img,cv.CV_16S)
Scale_abs = cv.convertScaleAbs(result)
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(Scale_abs,cmap = plt.cm.gray),plt.title('Laplacian')
plt.xticks([]), plt.yticks([])
Canny边缘检测
Canny边缘检测是一种基于灰度突变的边缘检测算法,主要用于提取图像中不连续部分的特征,以便将图像分割成不同的子区域或轮廓线。
Canny边缘检测算法包括以下几个关键步骤:
- 高斯滤波:使用高斯滤波器对图像进行平滑处理,以减少图像噪声对边缘检测的影响。
- 像素梯度计算:计算图像中每个像素的梯度幅度和方向,这有助于确定边缘的强度和方向。
- 非极大值抑制:只保留梯度幅度最大的像素,抑制其他非极大值像素,这样可以减少边缘检测中的伪边缘。
- 滞后阈值处理:使用两个阈值(高阈值和低阈值)来确定真正的边缘,强边缘通过高阈值被直接标记,而弱边缘只有在连接到强边缘时才被标记。
- 孤立弱边缘抑制:去除那些没有连接到强边缘的孤立弱边缘,以得到更加清晰的边缘检测结果。
在OpenCV中要实现Canny检测使用的API:
canny = cv2.Canny(image, threshold1, threshold2)
- image:灰度图,
- threshold1: minval,较小的阈值将间断的边缘连接起来
- threshold2: maxval,较大的阈值检测图像中明显的边缘
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('ho.jpg',0)
lowThreshold = 0
max_lowThreshold = 100
canny = cv.Canny(img, lowThreshold, max_lowThreshold)
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(canny,cmap = plt.cm.gray),plt.title('Canny')
plt.xticks([]), plt.yticks([])
视频读写
创建读取视频的对象:
cap = cv.VideoCapture(filepath)
-
filepath: 视频文件路径
-
视频的属性信息
获取视频的某些属性:
retval = cap.get(propId)
- propId: 从0到18的数字,每个数字表示视频的属性
修改视频的属性信息:
cap.set(propId,value)
- proid: 属性的索引,与上面的表格相对应
-
value: 修改后的属性值
isornot = cap.isOpened()
-
若读取成功则返回true,否则返回False
-
获取视频的一帧图像
ret, frame = cap.read()
- ret: 若获取成功返回True,获取失败,返回False
-
Frame: 获取到的某一帧的图像
-
调用cv.imshow()显示图像,在显示图像时使用cv.waitkey()设置适当的持续时间,如果太低视频会播放的非常快,如果太高就会播放的非常慢,通常情况下我们设置25ms就可以了。
-
最后,调用cap.realease()将视频释放掉
创建视频写入的对象:
out = cv2.VideoWriter(filename,fourcc, fps, frameSize)
- filename:视频保存的位置
- fourcc:指定视频编解码器的4字节代码
- fps:帧率
-
frameSize:帧大小
eg:
import cv2 as cv
import numpy as np
cap = cv.VideoCapture("cat.wmv")
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
out = cv.VideoWriter('outpy.avi',cv.VideoWriter_fourcc('M','J','P','G'), 10, (frame_width,frame_height))
while(True):
# 获取视频的每一帧图像
ret, frame = cap.read()
if ret == True: # 将每一帧图像写入到输出文件
out.write(frame)
else:
break
cv.destroyAllWindows()