@[TOC](OpenCV入门(十四)快速学会OpenCV 13 边际检测)
作者:Xiou
1.边际检测概述
边际检测是图画处理和核算机视觉中的基本问题,边际检测的意图是标识数字图画中亮度改变显着的点。图画特点中的显着改变一般反映了特点的重要事情和改变,包含深度不接连、表面方向不接连、物质特点改变和场景照明改变。边际检测特征是提取中的一个研讨范畴。图画边际检测大起伏地削减了数据量,并且剔除了能够以为不相关的信息,保留了图画重要的结构特点。
有许多办法能够用于边际检测,绝大部分能够划分为两类:依据查找的一类和依据零穿越的一类。依据查找的办法经过寻觅图画一阶导数中的最大值和最小值来检测鸿沟,一般是将鸿沟定位在梯度最大的方向上。依据零穿越的办法经过寻觅图画二阶导数零穿越来寻觅鸿沟,一般是Laplacian过零点或许非线性差分表明的过零点。
人类视觉体系知道方针的进程分为两步:首要,把图画边际与背景分离出来;然后,到图画的细节,辨认出图画的概括。核算机视觉正是模仿人类视觉的进程。
因而,在检测物体边际时先对概括点进行粗略检测,然后经过链接规矩把原来检测到的概括点衔接起来,一起检测和衔接遗失的鸿沟点及去除虚伪的鸿沟点。图画的边际是图画的重要特征,是核算机视觉、模式辨认等的基础,因而边际检测是图画处理中一个重要的环节。 可是,边际检测是图画处理中的一个难题,因为实践景物图画的边际往往是各种类型的边际及它们含糊化后成果的组合,且实践图画信号存在噪声。噪声和边际都归于高频信号,很难用频带做取舍。
边际是指图画周围像素灰度有阶跃改变或房顶状改变的像素集合,存在于方针与背景、方针与方针、区域与区域、基元与基元之间。边际具有方向和起伏两个特征,沿边际走向,像素值改变比较平缓;笔直于边际走向,像素值改变比较剧烈,或许呈现阶跃状,也或许呈现斜坡状。
因而,边际能够分为两种:一种为阶跃性边际,两边的像素灰度值有着显着的不同;另一种为房顶状边际,位于灰度值从增加到削减的改变转折点。关于阶跃性边际,二阶方向导数在边际处呈零穿插;关于房顶状边际,二阶方向导数在边际处取极值。
图画边际检测技能是图画处理和核算机视觉等范畴最基本的问题,也是经典的技能难题之一。如何快速、精确地提取图画边际信息,一直是国内外的研讨热门,一起边际的检测也是图画处理中的一个难题。早期的经典算法包含边际算子办法、曲面拟合的办法、模板匹配办法、阈值法等。
近年来,跟着数学理论与人工智能技能的发展,呈现了许多新的边际检测办法,如Roberts、Laplacan、Canny等图画的边际检测办法。这些办法的运用关于高水平的特征提取、特征描绘、方针辨认和图画理解有严重的影响。可是,在成像处理的进程中投影、混合、失真和噪声等会导致图画含糊和变形,这使得人们一直致力于构造具有良好特性的边际检测算子。
图画边际检测首要包含以下5个过程:
(1)图画获取 (2)图画滤波 (3)图画增强 (4)图画检测 (5)图画定位
测试原图:
2.Roberts算子边际检测
Roberts算子又称为穿插微分算法,是依据穿插差分的梯度算法,经过局部差分核算检测边际线条。常用来处理具有峻峭的低噪声图画,当图画边际接近于正45度或负45度时,该算法处理效果更抱负。其缺点是对边际的定位不太精确,提取的边际线条较粗。
完结Roberts算子首要经过OpenCV中的filter2D函数来完结。这个函数的首要功能是经过卷积核完结对图画的卷积运算,声明如下:
def filter2D(src, ddepth, kernel, dst=None, anchor=None, delta=None,
borderType=None)
参数 src为输入图画, ddepth表明方针图画所需的深度, kernel表明卷积核。
代码实例:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 读取图画
img = cv.imread('test.jpg', cv.COLOR_BGR2GRAY)
rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
# 灰度化处理图画
grayImage = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# Roberts 算子
kernelx = np.array([[-1, 0], [0, 1]], dtype=int)
kernely = np.array([[0, -1], [1, 0]], dtype=int)
x = cv.filter2D(grayImage, cv.CV_16S, kernelx)
y = cv.filter2D(grayImage, cv.CV_16S, kernely)
# 转 uint8 ,图画交融
absX = cv.convertScaleAbs(x)
absY = cv.convertScaleAbs(y)
Roberts = cv.addWeighted(absX, 0.5, absY, 0.5, 0)
# 显现图形
titles = ['src', 'Roberts operator']
images = [rgb_img, Roberts]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
在上述代码中,咱们界说了函数Roberts,用来完结Roberts算子,其办法经过公式来完结。在调用Roberts之前,先运用库函数GaussianBlur进行高斯滤波。
输出成果:
3.Sobel算子边际检测
Sobel算子是经过离散微分办法求取图画边际的边际检测算子,Sobel算子(索贝尔算子)运用像素上、下、左、右邻域的灰度加权算法,依据在边际点处到达极值这一原理进行边际检测。该办法不但发生较好的检测效果,而且对噪声具有滑润作用,能够供给较为精确的边际方向信息。在技能上,它以离散型的差分算子来运算图画亮度函数的梯度近似值,缺点是Sobel算子并没有将图画的主题和背景严格区分隔。
换句话说,Sobel算子并不是依据图画灰度进行处理的,因为Sobel算子并没有严格地模拟人的视觉生理特性,因而图画概括的提取有时并不能让人满足。
OpenCV供给了对图画提取Sobel边际的Sobel函数,该函数声明如下:
Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
代码实例:
import cv2 as cv
import matplotlib.pyplot as plt
# 读取图画
img = cv.imread('test.jpg', cv.COLOR_BGR2GRAY)
rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
# 灰度化处理图画
grayImage = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# Sobel 算子
x = cv.Sobel(grayImage, cv.CV_16S, 1, 0)
y = cv.Sobel(grayImage, cv.CV_16S, 0, 1)
# 转 uint8 ,图画交融
absX = cv.convertScaleAbs(x)
absY = cv.convertScaleAbs(y)
Sobel = cv.addWeighted(absX, 0.5, absY, 0.5, 0)
# 用来正常显现中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 显现图形
titles = ['原始图画', 'Sobel 算子']
images = [rgb_img, Sobel]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
输出成果:
4.Prewitt算子边际检测
Prewitt边际算子是一种边际样板算子。样板算子由抱负的边际算子图画构成,顺次用边际样板去检测图画,与被检测区域最为相似的样板给出最大值,Prewitt算子和Sobel差不多,运用像素点上下、左右邻点灰度差,在边际处到达极值检测边际。其对噪声具有滑润作用,定位精度不够高。
Prewitt_X算子实践上是先对图画进行笔直方向的非归一化的均值滑润,然后进行水平方向的差分;可是Prewitt_Y算子实践上是先对图画进行水平方向的非归一化的均值滑润,然后进行笔直方向的差分。这便是Prewitt算子能够按捺噪声的原因。同理,咱们也能够得到对角上的Prewitt算子。
代码实例:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 读取图画
img = cv.imread('test.jpg', cv.COLOR_BGR2GRAY)
rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
# 灰度化处理图画
grayImage = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# Prewitt 算子
kernelx = np.array([[1,1,1],[0,0,0],[-1,-1,-1]],dtype=int)
kernely = np.array([[-1,0,1],[-1,0,1],[-1,0,1]],dtype=int)
x = cv.filter2D(grayImage, cv.CV_16S, kernelx)
y = cv.filter2D(grayImage, cv.CV_16S, kernely)
# 转 uint8 ,图画交融
absX = cv.convertScaleAbs(x)
absY = cv.convertScaleAbs(y)
Prewitt = cv.addWeighted(absX, 0.5, absY, 0.5, 0)
# 用来正常显现中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 显现图形
titles = ['原始图画', 'Prewitt 算子']
images = [rgb_img, Prewitt]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
输出成果:
5.LoG算子边际检测
LoG边际检测算子是由David Courtnay Marr和Ellen Hildreth(1980)一起提出的。因而,也称为边际检测算法或Marr & Hildreth算子。该算法首要对图画做高斯滤波,然后求其拉普拉斯(Laplacian)二阶导数,即图画与Laplacian of the Gaussian function进行滤波运算。
最后,经过检测滤波成果的零穿插(Zero crossings)能够获得图画或物体的边际。因而,也被业界简称为Laplacian-of-Gaussian(LoG)算子。
代码实例:
import cv2 as cv
import matplotlib.pyplot as plt
# 读取图画
img = cv.imread("test.jpg")
rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 先经过高斯滤波降噪
gaussian = cv.GaussianBlur(gray_img, (3, 3), 0)
# 再经过拉普拉斯算子做边际检测
dst = cv.Laplacian(gaussian, cv.CV_16S, ksize=3)
LOG = cv.convertScaleAbs(dst)
# 用来正常显现中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 显现图形
titles = ['原始图画', 'LOG 算子']
images = [rgb_img, LOG]
for i in range(2):
plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
该算法首要对图画做高斯滤波,然后求拉普拉斯(Laplacian)二阶导数,依据二阶导数的过零点来检测图画的鸿沟,即经过检测滤波成果的零穿插(Zero crossings)来获得图画或物体的边际。LoG算子实践上是把Gauss滤波和Laplacian滤波结合了起来,先滑润掉噪声,再进行边际检测。
输出成果:
6.Canny算子边际检测
Canny边际检测是一种运用多级边际检测算法检测边际的办法。1986年,John F. Canny发表了闻名的论文A Computational Approach to Edge Detection,在该论文中胪陈了如何进行边际检测。OpenCV供给了函数cv2.Canny()完结Canny边际检测。
过程:
运用高斯滤波器, 滑润图画, 消除噪声; 核算图画中每个像素点的梯度强度和方向; 运用没极大值按捺 (Non-Maximum Suppression) 消除边际检测带来的杂散呼应; 运用双阈值检测 (Double Threshold) 来确认实在和潜在的边际; 经过按捺孤立的弱边际最终完结边际检测。
6.1运用高斯滤波去除图画噪声
因为图画边际非常容易遭到噪声的搅扰,因而为了避免检测到错误的边际信息,一般需要对图画进行滤波以去除噪声。滤波的意图是滑润一些纹路较弱的非边际区域,以便得到更精确的边际。在实践处理进程中,一般选用高斯滤波去除图画中的噪声。
6.2核算梯度
在这里,咱们关注梯度的方向,梯度的方向与边际的方向是笔直的。边际检测算子返回水平方向的Gx和笔直方向的Gy。梯度的起伏G和方向(用视点值表明)为:
梯度的方向总是与边际笔直的,一般就近取值为水平(左、右)、笔直(上、下)、对角线(右上、左上、左下、右下)等8个不同的方向。
因而,在核算梯度时,咱们会得到梯度的起伏和视点(代表梯度的方向)两个值。
6.3非极大值按捺
在获得了梯度的起伏和方向后,遍历图画中的像素点,去除一切非边际的点。在具体完结时,逐个遍历像素点,判别当时像素点是否是周围像素点中具有相同梯度方向的最大值,并依据判别成果决议是否按捺该点。经过以上描绘可知,该过程是边际细化的进程。
6.4运用双阈值确认边际
完结上述过程后,图画内的强边际已经在当时获取的边际图画内。可是,一些虚边际或许也在边际图画内。这些虚边际或许是实在图画发生的,也或许是因为噪声所发生的。关于后者,有必要将其剔除。
设置两个阈值,其间一个为高阈值maxVal,另一个为低阈值minVal。依据当时边际像素的梯度值(指的是梯度起伏,下同)与这两个阈值之间的联系,判别边际的特点。
6.5 代码实例
import cv2
import numpy as np
# 读取图片, 并转换成灰度图
img = cv2.imread("test.jpg", cv2.IMREAD_GRAYSCALE)
# Canny边际检测
out1 = cv2.Canny(img, 50, 150)
out2 = cv2.Canny(img, 100, 150)
# 合并
canny = np.hstack((out1, out2))
# 展现图片
cv2.imshow("src", img)
cv2.imshow("canny", canny)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出成果: