您的位置:首页 > 编程语言 > Python开发

计算机视觉基础-图像处理(边缘检测)cpp+python

2020-06-01 05:10 85 查看

6.1 简介

6.1.1 什么是边缘?
边缘是图像强度函数快速变化的地方

6.1.2 如何检测边缘?
为了检测边缘,我们需要检测图像中的不连续性,可以使用导数来检测不连续性。
如上图所示,上图的第一幅图表示一张数字图片,我们对水平红线处进行求导,便可得到上图二中的关系,可以看到在边缘处有着较大的跳变。但是,导数也会受到噪声的影响,因此建议在求导数之前先对图像进行平滑处理(上图三)。

但是,导数也会受到噪声的影响,因此建议在求导数之前先对图像进行平滑处理。 然后我们可以使用遮罩使用卷积来检测边缘。 同样,我不打算讨论数学部分,这里我们仅关注实现细节。

6.2 学习目标

  • 掌握Sobel边缘检测的原理
  • 掌握Canny边缘检测的原理

6.3 内容介绍

  • Sobel,Canny算子介绍
  • OpenCV 代码实践

6.4 算法理论介绍与推荐

6.4.1 Sobel算子
Sobel 算子是一个主要用作边缘检测的离散微分算子 (discrete differentiation operator)。 它Sobel算子结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。

Sobel卷积因子为:
该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以A代表原始图像,Gx及Gy分别代表经横向及纵向边缘检测的图像灰度值,其公式如下:
具体计算如下:

Gx = (-1)*f(x-1, y-1) + 0*f(x,y-1) + 1*f(x+1,y-1)

+(-2)*f(x-1,y) + 0*f(x,y)+2*f(x+1,y)

+(-1)*f(x-1,y+1) + 0*f(x,y+1) + 1*f(x+1,y+1)

= [f(x+1,y-1)+2*f(x+1,y)+f(x+1,y+1)]-[f(x-1,y-1)+2*f(x-1,y)+f(x-1,y+1)]

Gy =1* f(x-1, y-1) + 2*f(x,y-1)+ 1*f(x+1,y-1)

+0*f(x-1,y) 0*f(x,y) + 0*f(x+1,y)

+(-1)*f(x-1,y+1) + (-2)*f(x,y+1) + (-1)*f(x+1, y+1)

= [f(x-1,y-1) + 2f(x,y-1) + f(x+1,y-1)]-[f(x-1, y+1) + 2*f(x,y+1)+f(x+1,y+1)]

其中f(a,b), 表示图像(a,b)点的灰度值;

图像的每一个像素的横向及纵向灰度值通过以下公式结合,来计算该点灰度的大小:

通常,为了提高效率 使用不开平方的近似值:

如果梯度G大于某一阀值 则认为该点(x,y)为边缘点。然后可用以下公式计算梯度方向

Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。

6.4.2普利维特算子(Prewitt operate):

除sobel边缘检测外 还有Prewitt算子, 它的卷积因子如下:
其他计算 和sobel差不多;

Prewitt算子利用像素点上下、左右邻点灰度差,在边缘处达到极值检测边缘。对噪声具有平滑作用,定位精度不够高。

6.4.3罗伯茨交叉边缘检测(Roberts Cross operator)

卷积因子如下:
灰度公式为:

近似公式为:

具体计算如下:

G(x,y)=abs(f(x,y)-f(x+1,y+1))+abs(f(x,y+1)-f(x+1,y))

灰度方向 计算公式为:

Roberts算子采用对角线方向相邻两像素之差近似梯度幅值检测边缘。检测水平和垂直边缘的效果好于斜向边缘,定位精度高,对噪声敏感

6.4.4 Canny边缘检测
Canny边缘检测于1986年由JOHN CANNY首次在论文《A Computational Approach to Edge Detection》中提出,就此拉开了Canny边缘检测算法的序幕。

Canny边缘检测是从不同视觉对象中提取有用的结构信息并大大减少要处理的数据量的一种技术,目前已广泛应用于各种计算机视觉系统。Canny发现,在不同视觉系统上对边缘检测的要求较为类似,因此,可以实现一种具有广泛应用意义的边缘检测技术。边缘检测的一般标准包括:

  • 以低的错误率检测边缘,也即意味着需要尽可能准确的捕获图像中尽可能多的边缘。
  • 检测到的边缘应精确定位在真实边缘的中心。
  • 图像中给定的边缘应只被标记一次,并且在可能的情况下,图像的噪声不应产生假的边缘。

为了满足这些要求,Canny使用了变分法。Canny检测器中的最优函数使用四个指数项的和来描述,它可以由高斯函数的一阶导数来近似。

在目前常用的边缘检测方法中,Canny边缘检测算法是具有严格定义的,可以提供良好可靠检测的方法之一。由于它具有满足边缘检测的三个标准和实现过程简单的优势,成为边缘检测最流行的算法之一。

完成一个Canny边缘检测算法可以分为以下四步:
1.高斯滤波
边缘检测结果容易受到图像噪声的影响,图片中一些噪声会大大影像边缘检测。因此为了使图像平滑,可以用高斯滤波器内核与图像进行卷积。 此步骤将使图像稍微平滑,以减少边缘检测器上明显噪声的影响。

大小为(2k+1)×(2k+1)的高斯滤波器核的方程式为:

注意,选择高斯核的大小会影响检测器的性能。 尺寸越大,检测器对噪声的灵敏度越低。 此外,随着高斯滤波器核大小的增加,用于检测边缘的定位误差将略有增加。一般5x5是一个比较不错的trade off。

2.计算梯度强度和方向
进行高斯滤波后,图像中的边缘可以指向各个方向,接下来使用四个算子来检测图像中的水平、垂直和对角边缘。边缘检测的算子(如Roberts,Prewitt,Sobel等)返回水平Gx和垂直Gy方向的一阶导数值,由此便可以确定像素点的梯度G和方向θ

其中G为梯度强度, θ表示梯度方向,arctan为反正切函数。通过上式我们可以得到一个梯度矩阵G和方向矩阵 θ。

【例3】角度的计算

见下图,假设 θ有四条线,分别是0,45,90,135度线(0度和180重合,是一条线)。需要通过(2)式求出的θ进行近似,分类到这四条线或四个角度中。

比如计算出的θ=91度,则应将其归类到90–270度方向
3.非极大值抑制(NMS)
在每一点上,邻域中心与沿着其对应的梯度方向的两个像素相比,若中心像素为最大值,则保留,否则中心置0,这样可以抑制非极大值,保留局部梯度最大的点,以得到细化的边缘。

对图像进行梯度计算后,仅仅基于梯度值提取的边缘仍然很模糊。对边缘有且应当只有一个准确的响应。而非极大值抑制则可以帮助将局部最大值之外的所有梯度值抑制为0。非极大值抑制是一种边缘稀疏技术,非极大值抑制的作用在于“瘦”边。直观上地看,对第二步得到的图片,边缘由粗变细了。

4. 用双阈值算法检测和连接边缘
一张图解释双阈值算法检测:
双阈值法非常简单,我们假设两类边缘:经过非极大值抑制之后的边缘点中,梯度值超过TH的称为强边缘,梯度值小于TH大于TL的称为弱边缘,梯度小于TL的不是边缘。

可以肯定的是,强边缘必然是边缘点,因此必须将T1设置的足够高,以要求像素点的梯度值足够大(变化足够剧烈),而弱边缘可能是边缘,也可能是噪声,如何判断呢?当弱边缘的周围8邻域有强边缘点存在时,就将该弱边缘点变成强边缘点,以此来实现对强边缘的补充。实际中人们发现T1:T2=2:1的比例效果比较好,其中T1可以人为指定,也可以设计算法来自适应的指定,比如定义梯度直方图的前30%的分界线为T1。检查8邻域的方法叫边缘滞后跟踪,连接边缘的办法还有区域生长法等等。

6.5 基于OpenCV的实现

6.5.1 Sobel算子
opencv使用sobel 算子的方法是cv2.Sobel

# -*- coding: utf-8 -*-
"""
cv2.Sobel(src, #参数是需要处理的图像;
ddepth, #图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度
dx, #dx和dy表示的是求导的阶数,0表示这个方向上没有求导,一般为0、1、2。
dy[,
dst[, #输出图片
ksize[,#Sobel算子的大小,必须为1、3、5、7。
scale[, #缩放导数的比例常数,默认情况下没有伸缩系数;
delta[, #可选的增量,将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中;
borderType #判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。
]]]]])
"""
import cv2
import numpy as np
from matplotlib import pyplot as plt

#读图
img = cv2.imread('IMG/test.jpg',0)

sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)

#画图
plt.subplot(1,3,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])

plt.subplot(1,3,2),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])

plt.subplot(1,3,3),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])

plt.show()


6.5.2 Canny算法

# -*- coding: utf-8 -*-

"""
cv2.Canny(image,            # 输入原图(必须为单通道图)
threshold1,
threshold2,       # 较大的阈值2用于检测图像中明显的边缘
[, edges[,
apertureSize[,    # apertureSize:Sobel算子的大小
L2gradient ]]])   # 参数(布尔值):
true: 使用更精确的L2范数进行计算(即两个方向的倒数的平方和再开放),
false:使用L1范数(直接将两个方向导数的绝对值相加)。
"""

import cv2
import numpy as np
import matplotlib.pyplot as plt

original_img = cv2.imread("IMG/test.jpg", 0)

# canny边缘检测
img1 = cv2.GaussianBlur(original_img,(3,3),0)
canny = cv2.Canny(img1, 50, 150)

# 画图
plt.subplot(1,2,1),plt.imshow(original_img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])

plt.subplot(1,2,2),plt.imshow(canny,cmap = 'gray')
plt.title('Canny'), plt.xticks([]), plt.yticks([])
plt.show()

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: