python+opencv-15 图像梯度与边缘检测

本文介绍了图像处理中的边缘检测技术,包括Sobel、Scharr、Canny和Laplacian算子,以及高斯拉普拉斯边缘检测。Sobel和Scharr算子用于计算图像梯度,Canny边缘检测算法通过多级处理得到精确边缘,Laplacian算子则是一种二阶导数算子,具有旋转不变性。这些方法在计算机视觉领域常用于图像特征提取和边缘定位。

图像梯度与边缘检测

图像梯度是一种描述图像像素之间差异的方法,可以作为图像的一种特征来表征图像。图像梯度计算的是图像的边缘信息,它能够反应图像像素变化的速度,如灰度值变化较大的地方,梯度值也较大。图像梯度就是指像素的一阶导数,在图像处理中可以通过计算像素值的差近似导数值。图像边缘是指灰度值发生急剧变化的地方,边缘检测的目的是在不破坏图像信息的条件下,减少图像的数据量,绘制出其边缘线图。

Soble算子

Soble算子一种离散型差分算子,用来运算图像灰度函数的梯度的近似值。Soble算子是典型的基于一阶导数的边缘监测算子,由于该算子中引入了类似局部平均的运算,因此对噪声具有平滑作用,能很好的消除噪声影响。
Soble算子包含两组 3 ∗ 3 3*3 33的矩阵,分别为横向及纵向模板,将之与图像进行平面卷积,即可分别得出横向及纵向的灰度差分近似值。
Soble算子的水平方向的模板为: G x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] × s r c {{G}_{x}}=\left[ \begin{matrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \\ \end{matrix} \right]\times src Gx=121000121×src
Soble算子的垂直方向的模板为: G y = [ − 1 − 2 − 1 0 0 0 1 2 1 ] × s r c {{G}_{y}}=\left[ \begin{matrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1 \\ \end{matrix} \right]\times src Gy=101202101×src

import cv2 as cv

image = cv.imread("lena.jpg", 0)

Sobelx = cv.Sobel(image, cv.CV_64F, 1, 0)
Sobelx = cv.convertScaleAbs(Sobelx)

Sobely = cv.Sobel(image, cv.CV_64F, 0, 1)
Sobely = cv.convertScaleAbs(Sobely)

Sobelxy = cv.Sobel(image, cv.CV_64F, 1, 1)
Sobelxy = cv.convertScaleAbs(Sobelxy)

Sobelxy_my = cv.addWeighted(Sobelx, 0.5, Sobely, 0.3, 0)

cv.imshow("image", image)
cv.imshow("Sobelx", Sobelx)
cv.imshow("Sobely", Sobely)
cv.imshow("Sobelxy", Sobelxy)
cv.imshow("Sobelxy_my", Sobelxy_my)

cv.waitKey()
cv.destroyAllWindows()

在这里插入图片描述

Scharr算子

Scharr算子可被视为Sobel算子的改进,具有与Sobel算子相同的计算速度,但是其精度更高。
Scharr算子由两个卷积核构成,且这两个卷积核不可分离。图像与水平方向的卷积运算反应垂直方向上的边缘强度,图像与垂直方向的卷积运算反应水平方向上的边缘强度。
s c h a r r x = [ 3 0 − 3 10 0 − 10 3 0 − 3 ] {{scharr}_{x}}=\left[ \begin{matrix} 3 & 0 & -3 \\ 10 & 0 & -10 \\ 3 & 0 & -3 \\ \end{matrix} \right] scharrx=31030003103 s c h a r r y = [ 3 10 3 0 0 0 − 3 − 10 − 3 ] {{scharr}_{y}}=\left[ \begin{matrix} 3 & 10 & 3 \\ 0 & 0 & 0 \\ -3 & -10 & -3 \\ \end{matrix} \right] scharry=30310010303

import cv2 as cv

image = cv.imread("lena.jpg", 0)

Scharrx = cv.Scharr(image, cv.CV_64F, 1, 0)
Scharrx = cv.convertScaleAbs(Scharrx)

Scharry = cv.Scharr(image, cv.CV_64F, 0, 1)
Scharry = cv.convertScaleAbs(Scharry)

Scharr_Sobel_x = cv.Sobel(image, cv.CV_64F, 1, 0, -1)
Scharr_Sobel_x = cv.convertScaleAbs(Scharr_Sobel_x)

Scharr_Sobel_y = cv.Sobel(image, cv.CV_64F, 0, 1, -1)
Scharr_Sobel_y = cv.convertScaleAbs(Scharr_Sobel_x)


Scharrxy_my = cv.addWeighted(Scharr_Sobel_x, 0.5, Scharr_Sobel_y, 0.3, 0)

cv.imshow("image", image)
cv.imshow("Scharrx", Scharrx)
cv.imshow("Scharry", Scharry)
cv.imshow("Scharrxy_my", Scharrxy_my)

cv.waitKey()
cv.destroyAllWindows()

在这里插入图片描述

Canny边缘检测

Canny边缘检测是一种十分流行的边缘检测算法,它使用一种多级边缘检测算法,可以更好的检测出图像的边缘信息。
算法步骤如下:

  1. 去燥。过滤图像的噪声,可以提升边缘检测的准确性。
  2. 计算梯度的幅度与方向。
  3. 非极大值抑制。
  4. 确定边缘信息。

详细说明如下:

  1. 应用高斯滤波去除图像的噪声。噪声对图像的边缘信息影响很大,所以一般需要对图像的非边缘区域的噪声进行平滑处理。
  2. 采用Sobel算子计算图像边缘的幅度。图像矩阵I分别与水平方向上卷积核和垂直方向上的卷子核得到dx和dy,然后利用平方和的开方 m a g n i t u d e = d x 2 + d y 2 magnitude=\sqrt {dx^2+dy^2} magnitude=dx2+dy2 得到边缘强度。之后利用计算出的dx和dy,计算出梯度方向 a n g l e = a r c t a n 2 ( d y , d x ) angle=arctan2(dy,dx) angle=arctan2(dy,dx)
  3. 对每一位置进行非极大值抑制的处理。逐一遍历像素点,判断当前像素点是否是周围像素点中具有相同梯度方向上的极大值。如果该点是极大值,则保留该点,否则将其归零。这种操作可以实现边缘信息的细化。
  4. 双阈值的滞后阈值处理。常用的方法是全局阈值分割和局部自适应阈值分割。滞后阈值分割是使用高阈值和低阈值两个阈值,按照以下三个规则进行边缘阈值处理。

即:

  • 边缘强度大于高阈值的那些点作为确定边缘点。
  • 边缘强度比低阈值小的那些点被剔除。
  • 边缘强度在低阈值和高阈值之间的那些点,只有这些点能按某一路径与确定边缘点相连时,才可以作为边缘点被接受。而组成这一路径的所有点的边缘强度逗比低阈值要大。即首先选定边缘强度大于高阈值的所有确定边缘点,然后在边缘强度大于低阈值的情况下尽可能延长边缘。
import cv2 as cv

image = cv.imread("lena.jpg", 0)

edg1 = cv.Canny(image, 30, 100)
edg2 = cv.Canny(image, 100, 200)
edg3 = cv.Canny(image, 200, 255)

cv.imshow("image", image)
cv.imshow("edg1", edg1)
cv.imshow("edg2", edg2)
cv.imshow("edg3", edg3)

cv.waitKey()
cv.destroyAllWindows()

在这里插入图片描述

Laplacian算子

Laplacian算子是一种二阶导数算子,具有旋转不变性,可以满足不同方向上的边缘监测要求。
∇ 2 f ( x , y ) = ∂ 2 f ( x , y ) ∂ 2 x + ∂ 2 f ( x , y ) ∂ 2 y {{\nabla }^{2}}f(x,y)=\frac{{{\partial }^{2}}f(x,y)}{{{\partial }^{2}}x}+\frac{{{\partial }^{2}}f(x,y)}{{{\partial }^{2}}y} 2f(x,y)=2x2f(x,y)+2y2f(x,y) ≈ ∂ ( f ( x + 1 , y ) − f ( x , y ) ) ∂ x + ∂ ( f ( x , y + 1 ) − f ( x , y ) ) ∂ y \approx \frac{\partial (f(x+1,y)-f(x,y))}{\partial x}+\frac{\partial (f(x,y+1)-f(x,y))}{\partial y} x(f(x+1,y)f(x,y))+y(f(x,y+1)f(x,y)) ≈ f ( x + 1 , y ) − f ( x , y ) − ( f ( x , y ) − f ( x − 1 , y ) ) + f ( x , y + 1 ) − f ( x , y ) − ( f ( x , y ) − f ( x , y − 1 ) ) \approx f(x+1,y)-f(x,y)-(f(x,y)-f(x-1,y))+f(x,y+1)-f(x,y)-(f(x,y)-f(x,y-1)) f(x+1,y)f(x,y)(f(x,y)f(x1,y))+f(x,y+1)f(x,y)(f(x,y)f(x,y1)) ≈ f ( x + 1 , y ) + f ( x − 1 , y ) + f ( x , y − 1 ) + f ( x , y + 1 ) − 4 f ( x , y ) \approx f(x+1,y)+f(x-1,y)+f(x,y-1)+f(x,y+1)-4f(x,y) f(x+1,y)+f(x1,y)+f(x,y1)+f(x,y+1)4f(x,y)
下式中的 I 0 I_0 I0 3 ∗ 3 3*3 33的拉普拉斯算子:
I 0 = [ 0 − 1 0 − 1 4 − 1 0 − 1 0 ] {{I}_{0}}=\left[ \begin{matrix} 0 & -1 & 0 \\ -1 & 4 & -1 \\ 0 & -1 & 0 \\ \end{matrix} \right] I0=010141010 I 0 = [ 0 1 0 1 − 4 1 0 1 0 ] {{I}_{0}}=\left[ \begin{matrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \\ \end{matrix} \right] I0=010141010

import cv2 as cv

image = cv.imread("lena.jpg", 0)

laplacian = cv.Laplacian(image, cv.CV_64F)
laplacian = cv.convertScaleAbs(laplacian)

cv.imshow("image", image)
cv.imshow("laplacian", laplacian)

cv.waitKey()
cv.destroyAllWindows()

在这里插入图片描述

高斯拉普拉斯边缘检测

拉普拉斯边缘检测算子没有对图像做平滑处理,会对噪声产生明显的影响,一种处理方法是先进行高斯平滑,再与拉普拉斯进行卷积,但是这样要做两次卷积,计算复杂度较大。另一种是利用二维高斯函数的拉普拉斯变换。高斯拉普拉斯边缘检测的效果与先进行高斯平滑再进行拉普拉斯边缘检测的效果是一样的。

import numpy as np
import math
import cv2 as cv
from scipy import signal


def createLoGKernel(sigma, kSize):
    winH, winW = kSize
    logKernel = np.zeros(kSize, np.float32)
    sigmaSquare = pow(sigma, 2.0)

    centerH = (winH - 1)/2
    centerW = (winW - 1)/2
    for r in range(winH):
        for c in range(winW):
            norm2 = pow(r - centerH, 2.0) + pow(c - centerW, 2.0)
            logKernel[r][c] = 1.0/sigmaSquare*(norm2/sigmaSquare - 2)*math.exp(-norm2/(2*sigmaSquare))
    return logKernel

def LoG(image, sigma, kSize, _boundary='fill', _filValue=0):
    logKernel = createLoGKernel(sigma, kSize)
    img_conv_log = signal.convolve2d(image, logKernel, 'same', boundary=_boundary)
    return img_conv_log

def edg_binary(img):
    edge = np.copy(img)
    edge[edge >= 0] = 0
    edge[edge < 0] = 255
    edge = edge.astype(np.uint8)
    return edge

image = cv.imread("lena.jpg", 0)

cv.imshow("image", image)

img1 = LoG(image, 2, (7, 7), 'symm')
img2 = LoG(image, 2, (11, 11), 'symm')
img3 = LoG(image, 2, (13, 13), 'symm')

L1 = edg_binary(img1)
L2 = edg_binary(img2)
L3 = edg_binary(img3)

cv.imshow("L1", L1)
cv.imshow("L2", L2)
cv.imshow("L3", L3)

cv.waitKey()
cv.destroyAllWindows()

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值