①滤波:边缘检测算法主要是基于图像强度的一阶和二阶导数,但导数的计算对噪声很敏感,因此必须使用滤波器来改善与噪声有关的边缘检测器的性能。需要指出,大多数滤波器在降低噪声的同时也导致了边缘强度的损失,因此,增强边缘和降低噪声之间需要折中。 ②增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将邻域(或局部)强度值有显著变化的点突显出来。边缘增强一般是通过计算梯度幅值来完成的。 ③检测:在图像中有许多点的梯度幅值比较大,而这些点在特定的应用领域中并不都是边缘,所以应该用某种方法来确定哪些点是边缘点。最简单的边缘检测判据是梯度幅值阈值判据。 ④定位:如果某一应用场合要求确定边缘位置,则边缘的位置可在子像素分辨率上来估计,边缘的方位也可以被估计出来。在边缘检测算法中,前三个步骤用得十分普遍。这是因为大多数场合下,仅仅需要边缘检测器指出边缘出现在图像某一像素点的附近,而没有必要指出边缘的精确位置或方向。
边 缘 : 图 像 上 灰 度 级 变 化 很 快 的 点 的 集 合 \red{边缘:图像上灰度级变化很快的点的集合} 边缘:图像上灰度级变化很快的点的集合 1.导数,连续函数上某点斜率,导数越大表示变化率越大,变化率越大的地方就越是“边缘”,但是在计算机中不常用,因为在斜率90度的地方,导数无穷大,计算机很难表示这些无穷大的东西。 2.微分,连续函数上x变化了dx,导致y变化了dy,dy值越大表示变化的越大,那么计算整幅图像的微分,dy的大小就是边缘的强弱了。 微分与导数的关系:dy = f '(x) dx 在连续函数里叫微分,因为图像是离散的,叫差分,和微分是一个意思,也是求变化率。 差分 :f '(x) = f(x + 1) - f(x),用后一项减前一项。 按先后排列 -f(x) + f(x + 1) 提出系数 [-1, 1] 作为滤波模板,跟原图 f(x) 做卷积运算就可以检测边缘了
S o b e l 边 缘 检 测 算 子 \red{Sobel 边缘检测算子} Sobel边缘检测算子 用 f '(x) = f(x + 1) - f(x - 1) 近似计算一阶差分。 排好序:[-1 * f(x-1),0 * f(x),1 * f(x+1)] 提出系数:[-1, 0, 1]
P r e w i t t 边 缘 检 测 算 子 \red{Prewitt 边缘检测算子} Prewitt边缘检测算子 二维情况下 -1, 0, 1 -1, 0, 1 -1, 0, 1 L a p l a c i a n 算 子 \red{Laplacian算子} Laplacian算子 拉普拉斯是用二阶差分计算边缘的,看连续函数的情况下 在一阶微分图中极大值或极小值处,认为是边缘。 在二阶微分图中极大值和极小值之间的过 0 点,被认为是边缘。 拉普拉斯算子推导: 一阶差分:f '(x) = f(x) - f(x - 1) 二阶差分:f '(x) = (f(x + 1) - f(x)) - (f(x) - f(x - 1)) 化简后:f '(x) = f(x - 1) - 2 f(x)) + f(x + 1) 提取前面的系数:[1, -2, 1] 二维的情况下,同理可得 f '(x, y) = -4 f(x, y) + f(x-1, y) + f(x+1, y) + f(x, y-1) + f(x, y+1) 提取各个系数 0, 1, 0 1, -4, 1 0, 1, 0 考虑两个斜对角的情况 1, 1, 1 1, -8, 1 1, 1, 1
并且我们可以得出结论:一阶导数的幅度可用于检测图像中的某个点是否存在一个边缘,二阶导数可以用于确定一个边缘像素位于该边缘的暗的一侧还是亮的一侧。
那么这是理想情况下的图片边缘,如果图片有噪声的话,其边缘函数则为 微弱的可见噪声对检测边缘所用的两个关键导数的严重影响的这一事实,是我们应记住的一个重要问题。特别地,在类似于我们刚刚讨论的水平的噪声很可能存在的应用中,使用导数之前的图像平滑处理是应该认真考虑的问题。
许多边缘检测操作都是基于亮度的一阶导数——这样就得到了原始数据亮度的梯度。使用这个信息我们能够在图像的亮度梯度中搜寻峰值。如果 I(x) 表示点 x 的亮度,I′(x) 表示点 x 的一阶导数(亮度梯度),这样我们就会发现: 对于更高性能的图像处理来说,一阶导数能够通过带有掩码的原始数据(1维)卷积计算得到。
其它一些边缘检测操作是基于亮度的二阶导数。这实质上是亮度梯度的变化率。在理想的连续变化情况下,在二阶导数中检测过零点将得到梯度中的局部最大值。另一方面,二阶导数中的峰值检测是边线检测,只要图像操作使用一个合适的尺度表示。如上所述,边线是双重边缘,这样我们就可以在边线的一边看到一个亮度梯度,而在另一边看到相反的梯度。这样如果图像中有边线出现的话我们就能在亮度梯度上看到非常大的变化。为了找到这些边线,我们可以在图像亮度的二阶导数中寻找过零点。如果 I(x) 表示点 x 的亮度,I′′(x) 表示点 x 亮度的二阶导数,那么: 同样许多算法也使用卷积掩码快速处理图像数据:
一阶::Roberts Cross算子,Prewitt算子,Sobel算子, Kirsch算子,罗盘算子; 二阶: Marr-Hildreth,在梯度方向的二阶导数过零点,Canny算子,Laplacian算子。
检测对角边缘的Prewitt和Sobel模板
平滑滤波器用于模糊处理和降低噪声。模糊处理经常用于预处理任务中,例如在目标提取之前去除图像中的一些琐碎细节。
平滑线性滤波器 滑线性空间滤波器的输出(响应)是包含在滤波器过滤核中像素的简单平均值,也成为均值滤波。 这种处理的结果降低了图像灰度的“尖锐”变化。
import cv2 import numpy as np img = cv2.imread('images/cat.jpg') cv2.imshow('images',img) """ blur—图像均值平滑滤波 函数原型:blur(src, ksize, dst=None, anchor=None, borderType=None) src:图像矩阵 ksize:滤波窗口尺寸 """ res = cv2.blur(img, (3, 3)) cv2.imshow('res', res) """ GaussianBlur—图像高斯平滑滤波 函数原型:GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None) src:图像矩阵 ksize:滤波窗口尺寸 sigmaX:标准差 """ res2 = cv2.GaussianBlur(img,(3,3),0) cv2.imshow('res2', res2) cv2.waitKey()非线性滤波器 这种滤波器的响应一滤波器过滤核中像素的排序为基础,然后使用统计排序的结果决定代替中心像素的值。这一类最知名的滤波器是中值滤波器,正如其名,它是将像素领域内灰度的中值代替该像素的值。中值滤波器对处理脉冲噪声非常有效。
import cv2 import numpy as np img = cv2.imread('images/lena_1.jpg') cv2.imshow('img', img) """ medianBlur—图像中值滤波 函数原型:medianBlur(src, ksize, dst=None) src:图像矩阵 ksize:滤波窗口尺寸 """ res = cv2.medianBlur(img, 5) cv2.imshow('res', res) cv2.waitKey()使用二阶微分进行图像锐化——拉普拉斯算子 由于拉普拉斯是一种微分算子,因此其应用强调的是图像中灰度的突变,并不强调灰度级缓慢变化的区域。将原图像和拉普拉斯图像叠加在一起的简单方法,可以复原背景特性并保持拉普拉斯锐化处理的效果。
如果所使用的定义具有负的中心系数,那么必须将原图像减去拉普拉斯变换后的图像而不是加上它,从而得到锐化的结果
import cv2 import numpy as np img = cv2.imread('images/lena.jpg') cv2.imshow('img',img) kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32) #kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]]) dst = cv2.filter2D(img, -1, kernel=kernel) cv2.imshow('res',dst) cv2.waitKey(0)使用一阶微分对(非线性)图像锐化——梯度
# coding=utf-8 import cv2 img = cv2.imread("images/lena.jpg") cv2.imshow('img',img) """ Sobel函数 Sobel(src, ddepth, dx, dy, dst=None, ksize=None, scale=None, delta=None, borderType=None) 前四个是必须的参数: 第一个参数是需要处理的图像; 第二个参数是图像的深度,-1表示采用的是与原图像相同的深度。目标图像的深度必须大于等于原图像的深度; 第三和第四:dx和dy表示的是求导的阶数,0表示这个方向上没有求导,一般为0、1、2。 其后是可选的参数 """ x = cv2.Sobel(img, -1, 0, 1) cv2.imshow('res',x) cv2.waitKey(0)Marr-Hildreth算法小结
用一个G(x,y)取样得到的nxn的高斯高斯低通滤波器对输入图像滤波。
-计算由第一步得到的图像的拉普拉斯 -找到步骤2所有图像的零交叉 -零交叉是Marr-Hildreth边缘检测方法的关键特征,实现简单,并且通常能给出好的结果。
import numpy as np import matplotlib.pyplot as plt import cv2 def edgesMarrHildreth(img, sigma): """ finds the edges using MarrHildreth edge detection method... :param im : input image :param sigma : sigma is the std-deviation and refers to the spread of gaussian :return: a binary edge image... """ size = int(2 * (np.ceil(3 * sigma)) + 1) x, y = np.meshgrid(np.arange(-size / 2 + 1, size / 2 + 1), np.arange(-size / 2 + 1, size / 2 + 1)) normal = 1 / (2.0 * np.pi * sigma ** 2) kernel = ((x ** 2 + y ** 2 - (2.0 * sigma ** 2)) / sigma ** 4) * np.exp( -(x ** 2 + y ** 2) / (2.0 * sigma ** 2)) / normal # LoG filter kern_size = kernel.shape[0] log = np.zeros_like(img, dtype=float) # applying filter for i in range(img.shape[0] - (kern_size - 1)): for j in range(img.shape[1] - (kern_size - 1)): window = img[i:i + kern_size, j:j + kern_size] * kernel log[i, j] = np.sum(window) log = log.astype(np.int64, copy=False) zero_crossing = np.zeros_like(log) # computing zero crossing for i in range(log.shape[0] - (kern_size - 1)): for j in range(log.shape[1] - (kern_size - 1)): if log[i][j] == 0: if (log[i][j - 1] < 0 and log[i][j + 1] > 0) or (log[i][j - 1] < 0 and log[i][j + 1] < 0) or ( log[i - 1][j] < 0 and log[i + 1][j] > 0) or (log[i - 1][j] > 0 and log[i + 1][j] < 0): zero_crossing[i][j] = 255 if log[i][j] < 0: if (log[i][j - 1] > 0) or (log[i][j + 1] > 0) or (log[i - 1][j] > 0) or (log[i + 1][j] > 0): zero_crossing[i][j] = 255 # plotting images fig = plt.figure() a = fig.add_subplot(1, 2, 1) imgplot = plt.imshow(log, cmap='gray') a.set_title('Laplacian of Gaussian') a = fig.add_subplot(1, 2, 2) imgplot = plt.imshow(zero_crossing, cmap='gray') string = 'Zero Crossing sigma = ' string += (str(sigma)) a.set_title(string) plt.show() return zero_crossing img = cv2.imread('images/17.jpg',0) img = edgesMarrHildreth(img,4)Canny目标 -低错误率。所有边缘都应被找到,并且应该没有伪相应,也就是检测到的边缘必须尽可能是真是的边缘 -边缘点应被很好的定位。已定位边缘必须尽可能接近真实边缘。也就是由检测器标记为边缘的点和真实边缘的中心之间的距离应该最小 -单一的边缘点响应。对于真实的边缘点,检测器仅应返回一个点。也就是真是边缘周围的局部最大数应该是最小的。这意味着在仅存一个单一边缘点到额位置,检测器不应指出多个边缘像素。 Canny算法步骤 1、用一个高斯滤波器平滑输入图像 2、计算梯度幅度图像和角度图像 3、对梯度幅度图像应用非最大抑制 4、用双阈值处理和连接分析来检测并连接边缘(这相对Marr-Hildreth优化了边缘的连接使得检测的边缘更加完整)
# coding=utf-8 import cv2 import numpy as np img = cv2.imread("images/17.jpg", 0) cv2.imshow('img',img) img = cv2.GaussianBlur(img, (3, 3), 0) canny = cv2.Canny(img, 50, 150) cv2.imshow('Canny', canny) cv2.waitKey(0) cv2.destroyAllWindows()可以看到,Canny算法明显提取边缘的效果要优于Marr-Hildreth算法,对边缘的连接也做的更好。