目录
此章节主要讲解图像梯度计算方法和边缘检测算法,分别主要是:sobel算子、scharr与lapkacian算子、canny边缘检测流程。
一.Sobel算子
A代表选取的卷积核。Gx,Gy分别代表水平梯度和垂直梯度
注:
图像的梯度算法就是卷积操作。公式如下:
这里与矩阵乘法有差异的地方在于求垂直梯度。
公式中的K代表卷积核
以下是水平梯度运算的方法。
Gx= 1*p3-1*p1+2*p6-2*p4+1*p9-1*p7
这个运算通过右边减去左边的区域,求出它的像素点的差异值。通过这个差异值,将求出的这个差异值视作水平方向的梯度
Gy = 1*p7-1*p1+2*p8-2*p2+1*p9-1*p3
G = Gx+Gy
垂直梯度的计算是从下减上的一个操作流程。以上是Sobel算子的一个运算规则方法。
Sobel算子的语法格式:
cv2.Sobel(src,ddepth,dx,dy,ksize)
- src:当前图像
- ddepth:图像的深度
- dx和dy分别表示水平和竖直方向
- ksize是Sobel算子的大小(指定核,一般是3×3或5×5)
注:
一般深度计算默认是-1,表示我输出的深度和输入的深度是一模一样的。
Sobel算子的ddepth 的参数共有五种类型分别为:cv2.CV_8U,cv2.CV_16S,cv2.CV_32F,cv2.CV_64F,cv2.CV_16U这五种类型的图像,分别表示8位无符号整数、16位有符号整数、32位浮点数、64位浮点数和16位无符号整数。
求水平梯度代码演示如下:
原图如下
代码如下:
import cv2
import numpy as np
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def cv_Xshow(image):
# Sobel算子第二个参数一共有5种,分别是:cv2.CV_8U,cv2.CV_16S,cv2.CV_32F,cv2.CV_64F,cv2.CV_16U
# 这五种类型的图像,分别表示8位无符号整数、16位有符号整数、32位浮点数、64位浮点数和16位无符号整数。
sobelx = cv2.Sobel(image,cv2.CV_64F,1,0,ksize=3)
cv_show(sobelx,'sobelx')
if __name__ == '__main__':
image = cv2.imread('10.png')
cv_Xshow(image)
运行代码后,求水平梯度图像如下:
注:
原本应该右边应该也出现白边但是这个图像并无这条边,原因是:白到黑是整数,黑到白就是负数,所有的负数会被截断成0,所以要取绝对值。
调整后的代码如下:
注:
cv2.convertScaleAbs的作用是把图像的像素值转换成绝对值,并返回一个图像
调整后代码的图像呈现如下:
import cv2
import numpy as np
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def cv_Xshow(image):
# Sobel算子第二个参数一共有5种,分别是:cv2.CV_8U,cv2.CV_16S,cv2.CV_32F,cv2.CV_64F,cv2.CV_16U
# 这五种类型的图像,分别表示8位无符号整数、16位有符号整数、32位浮点数、64位浮点数和16位无符号整数。
sobelx = cv2.Sobel(image,cv2.CV_64F,1,0,ksize=3)
# cv2.convertScaleAbs的作用是把图像的像素值转换成绝对值,并返回一个图像
sobelx = cv2.convertScaleAbs(sobelx)
cv_show(sobelx,'sobelx')
if __name__ == '__main__':
image = cv2.imread('10.png')
cv_Xshow(image)
求垂直梯度代码演示如下:
原图如下:
代码如下:
import cv2
import numpy as np
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def cv_Yshow(image):
sobelY = cv2.Sobel(image,cv2.CV_64F,0,1,ksize=3)
sobelY = cv2.convertScaleAbs(sobelY)
cv_show(sobelY,'sobelY')
if __name__ == '__main__':
image = cv2.imread('10.png')
cv_Yshow(image)
代码过后的图像如下:
完整图像梯度的代码如下:
import cv2
import numpy as np
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def cv_Xshow(image):
# Sobel算子第二个参数一共有5种,分别是:cv2.CV_8U,cv2.CV_16S,cv2.CV_32F,cv2.CV_64F,cv2.CV_16U
# 这五种类型的图像,分别表示8位无符号整数、16位有符号整数、32位浮点数、64位浮点数和16位无符号整数。
sobelx = cv2.Sobel(image,cv2.CV_64F,1,0,ksize=3)
# cv2.convertScaleAbs的作用是把图像的像素值转换成绝对值,并返回一个图像
sobelx = cv2.convertScaleAbs(sobelx)
return sobelx
def cv_Yshow(image):
sobelY = cv2.Sobel(image,cv2.CV_64F,0,1,ksize=3)
sobelY = cv2.convertScaleAbs(sobelY)
return sobelY
def cv_XYshow(image,sobelX,sobelY):
sobelXY = cv2.addWeighted(sobelX,0.5,sobelY,0.5,0)
cv_show(sobelXY,'sobelXY')
return None
if __name__ == '__main__':
image = cv2.imread('10.png')
cv_XYshow(image,cv_Xshow(image),cv_Yshow(image))
运行代码后的图像呈现如下:
注:
不建议直接进行计算,建议使用cv2.addWeighted进行梯度的计算。
直接计算梯度的代码演示如下:
import cv2
import numpy as np
src = cv2.imread('10.png')
sobelX = cv2.Sobel(src,cv2.CV_64F,1,0)
sobelY = cv2.Sobel(src,cv2.CV_64F,0,1)
sobelXY = cv2.Sobel(src,cv2.CV_64F,1,1)
cv2.imshow('sobelXY',sobelXY)
cv2.waitKey(0)
cv2.destroyAllWindows()
注:
从此可以看出来如果直接计算图像的呈现并不好。因此最好使用cv2.addWeighted进行水平和垂直的相加进行的一个比例分布。
综合运行梯度计算的代码如下:
原图如下:
代码如下:
import cv2
import numpy as np
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img = cv2.imread('2.jpg')
sobelX = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelX = cv2.convertScaleAbs(sobelX)
sobelY = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobelY = cv2.convertScaleAbs(sobelY)
sobelXY = cv2.addWeighted(sobelX,0.5,sobelY,0.5,0)
cv_show(sobelXY,'sobelXY')
代码运行后的图像呈现如下:
注:
这种计算梯度的方式可以取出一个图像的基本框架。
二.Scharr算子与laplacian算子
Scharr算子与Sobel本质没什么太大区别,基本原理依旧如同Sobel算子一样。唯一区别是Scharr算子核的数据要比Sobel算子的处理的数据更敏感(较大)。
接下来讲解讲解laplacian算子。
从图可以看出laplacian算子与前面二种算子有很大区别。
laplacian算子的计算方法如下:
laplacian算子计算方式是G = 1*p2+ 1*p4+1*p6+1*p8-4*p5
相当于拿中间的P6与周围边缘点进行比较,由于周围边缘点是4个都是正的,所以是中间的点为-4,这就是它与其他算子核不同的原因。
三种算子的程序如下:
原图:
代码如下:
import cv2
import numpy as np
# 不同算子的差异
img = cv2.imread("5.jpg")
gray = cv2.cvtColor(img,cv2.IMREAD_GRAYSCALE)
# sobel算子
sobelx = cv2.Sobel(gray,cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(gray,cv2.CV_64F,0,1,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
# Scharr算子
scharrx = cv2.Scharr(gray,cv2.CV_64F,1,0)
scharry = cv2.Scharr(gray,cv2.CV_64F,0,1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
# laplacian算子
laplacian = cv2.Laplacian(gray,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
res = np.hstack((sobelxy,scharrxy,laplacian))
cv2.imshow("res",res)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行代码后如下:
三.Canny边缘检测
以下是Canny边缘检测算法的步骤如下:
1)使用高斯滤波器,以平滑图像,滤除噪声。
2)计算图像中每个像素点的梯度强度和方向。
3)应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
4)应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
5)通过抑制孤立的弱边缘最终完成边缘检测。
1.高斯滤波器
2.梯度和方向
运用了Sobel算子
3.非极大值抑制
非极大值抑制有两种方法;
方法一:
方法二:为了简化计算,由于一个像素周围有八个像素,把一个像素的梯度方向离散为八个方向,这样就只需计算前后即可,不用插值了。
4.双阈值检测
Canny边缘检测算法演示代码如下:
import cv2
import numpy as np
img = cv2.imread('9.jpg',cv2.IMREAD_GRAYSCALE)
# 高斯滤波,降噪
img = cv2.GaussianBlur(img,(5,5),0)
# 边缘检测
# 第一个参数是图像,第二个参数是低阈值,第三个参数是高阈值
v1 = cv2.Canny(img,80,160)
v2 = cv2.Canny(img,50,200)
res = np.hstack((v1,v2))
cv2.imshow('res',res)
cv2.waitKey(0)
cv2.destroyAllWindows()
注:
从此可以看出来Canny边缘检测,你设置的阈值越大检测的东西检测边界的东西越少,设置阈值越小检测边界的东西越丰富。
Canny语法格式:cv2.Canny(图像,低阈值,高阈值)