python数字图像处理(三)边缘检测常用算子

在该文将介绍基本的几种应用于边缘检测的滤波器,首先我们读入saber用来做为示例的图像

#读入图像代码,在此之前应当引入必要的opencv matplotlib numpysaber = cv2.imread("saber.png")saber = cv2.cvtColor(saber,cv2.COLOR_BGR2RGB)plt.imshow(saber)plt.axis("off")plt.show()

使用上图作为滤波器使用的图形

Roberts 交叉梯度算子

Roberts交叉梯度算子由两个2×2" role="presentation" style="position: relative;">2×22×2的模版构成,如图。

[−1001]" role="presentation" style="position: relative;">[−1001][−1001]
[0−110]" role="presentation" style="position: relative;">[01−10][0−110]

对如图的矩阵分别才用两个模版相乘,并加在一起

[Z1Z2Z3Z4Z5Z6Z7Z8Z9]" role="presentation" style="position: relative;">⎡⎣⎢Z1Z4Z7Z2Z5Z8Z3Z6Z9⎤⎦⎥[Z1Z2Z3Z4Z5Z6Z7Z8Z9]

简单表示即
|z9−z5|+|z8−z6|" role="presentation" style="position: relative;">|z9−z5|+|z8−z6||z9−z5|+|z8−z6|。
可以简单用编程实现

首先将原图像进行边界扩展,并将其转换为灰度图。

gray_saber = cv2.cvtColor(saber,cv2.COLOR_RGB2GRAY)gray_saber = cv2.resize(gray_saber,(200,200))

在接下来的的代码中我们对图像都会进行边界扩展,是因为将图像增加宽度为1的图像能够使在计算3*3的算子时不用考虑边界情况

def RobertsOperator(roi):    operator_first = np.array([[-1,0],[0,1]])    operator_second = np.array([[0,-1],[1,0]])    return np.abs(np.sum(roi[1:,1:]*operator_first))+np.abs(np.sum(roi[1:,1:]*operator_second))def RobertsAlogrithm(image):    image = cv2.copyMakeBorder(image,1,1,1,1,cv2.BORDER_DEFAULT)    for i in range(1,image.shape[0]):        for j in range(1,image.shape[1]):            image[i,j] = RobertsOperator(image[i-1:i+2,j-1:j+2])    return image[1:image.shape[0],1:image.shape[1]]
Robert_saber = RobertsAlogrithm(gray_saber)
plt.imshow(Robert_saber,cmap="binary")plt.axis("off")plt.show()

python的在这种纯计算方面实在有些慢,当然这也是因为我过多的使用了for循环而没有利用好numpy的原因

我们可以看出Roberts算子在检测边缘效果已经相当不错了,但是它对噪声相当敏感,我们给它加上噪声在看看它的效果
噪声代码来源于stacksoverflow

def noisy(noise_typ,image):    if noise_typ == "gauss":        row,col,ch= image.shape        mean = 0        var = 0.1        sigma = var**0.5        gauss = np.random.normal(mean,sigma,(row,col,ch))        gauss = gauss.reshape(row,col,ch)        noisy = image + gauss        return noisy    elif noise_typ == "s&p":        row,col,ch = image.shape        s_vs_p = 0.5        amount = 0.004        out = np.copy(image)        num_salt = np.ceil(amount * image.size * s_vs_p)        coords = [np.random.randint(0, i - 1, int(num_salt))              for i in image.shape]        out[coords] = 1        num_pepper = np.ceil(amount* image.size * (1. - s_vs_p))        coords = [np.random.randint(0, i - 1, int(num_pepper)) for i in image.shape]        out[coords] = 0        return out    elif noise_typ == "poisson":        vals = len(np.unique(image))        vals = 2 ** np.ceil(np.log2(vals))        noisy = np.random.poisson(image * vals) / float(vals)        return noisy    elif noise_typ =="speckle":        row,col,ch = image.shape        gauss = np.random.randn(row,col,ch)        gauss = gauss.reshape(row,col,ch)                noisy = image + image * gauss        return noisy
dst = noisy("s&p",saber)plt.subplot(121)plt.title("add noise")plt.axis("off")plt.imshow(dst)plt.subplot(122)dst = cv2.cvtColor(dst,cv2.COLOR_RGB2GRAY)plt.title("Robert Process")plt.axis("off")dst = RobertsAlogrithm(dst)plt.imshow(dst,cmap="binary")plt.show()

这里我们可以看出Robert算子对于噪声过于敏感,在加了噪声后,人的边缘变得非常不清晰

Prewitt算子

Prewitt算子是一种3×3" role="presentation" style="position: relative;">3×33×3模板的算子,它有两种形式,分别表示水平和垂直的梯度.

垂直梯度

[−1−1−1000111]" role="presentation" style="position: relative;">⎡⎣⎢−101−101−101⎤⎦⎥[−1−1−1000111]

水平梯度

[−101−101−101]" role="presentation" style="position: relative;">⎡⎣⎢−1−1−1000111⎤⎦⎥[−101−101−101]

对于如图的矩阵起始值

[Z1Z2Z3Z4Z5Z6Z7Z8Z9]" role="presentation" style="position: relative;">⎡⎣⎢Z1Z4Z7Z2Z5Z8Z3Z6Z9⎤⎦⎥[Z1Z2Z3Z4Z5Z6Z7Z8Z9]

就是以下两个式子

|(z7+z8+z9)−(z1+z2+z3)|" role="presentation" style="position: relative;">|(z7+z8+z9)−(z1+z2+z3)||(z7+z8+z9)−(z1+z2+z3)|
|(z1+z4+z7)−(z3+z6+z9)|" role="presentation" style="position: relative;">|(z1+z4+z7)−(z3+z6+z9)||(z1+z4+z7)−(z3+z6+z9)|

1与-1更换位置对结果并不会产生影响

def PreWittOperator(roi,operator_type):    if operator_type == "horizontal":        prewitt_operator = np.array([[-1,-1,-1],[0,0,0],[1,1,1]])    elif operator_type == "vertical":        prewitt_operator = np.array([[-1,0,1],[-1,0,1],[-1,0,1]])    else:        raise("type Error")    result = np.abs(np.sum(roi*prewitt_operator))    return result    def PreWittAlogrithm(image,operator_type):    new_image = np.zeros(image.shape)    image = cv2.copyMakeBorder(image,1,1,1,1,cv2.BORDER_DEFAULT)    for i in range(1,image.shape[0]-1):        for j in range(1,image.shape[1]-1):            new_image[i-1,j-1] = PreWittOperator(image[i-1:i+2,j-1:j+2],operator_type)    new_image = new_image*(255/np.max(image))    return new_image.astype(np.uint8)

在PreWitt算子执行中要考虑大于255的情况,使用下面代码

    new_image = new_image*(255/np.max(image))

将其放缩到0-255之间,并使用astype将其转换到uint8类型

plt.subplot(121)plt.title("horizontal")plt.imshow(PreWittAlogrithm(gray_saber,"horizontal"),cmap="binary")plt.axis("off")plt.subplot(122)plt.title("vertical")plt.imshow(PreWittAlogrithm(gray_saber,"vertical"),cmap="binary")plt.axis("off")plt.show()

现在我们看一下Prewitt对噪声的敏感性

dst = noisy("s&p",saber)plt.subplot(131)plt.title("add noise")plt.axis("off")plt.imshow(dst)plt.subplot(132)plt.title("Prewitt Process horizontal")plt.axis("off")plt.imshow(PreWittAlogrithm(gray_saber,"horizontal"),cmap="binary")plt.subplot(133)plt.title("Prewitt Process vertical")plt.axis("off")plt.imshow(PreWittAlogrithm(gray_saber,"vertical"),cmap="binary")plt.show()

选择水平梯度或垂直梯度从上图可以看出对于边缘的影响还是相当大的.

Sobel算子

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

[−1−2−1000121]" role="presentation" style="position: relative;">⎡⎣⎢−101−202−101⎤⎦⎥[−1−2−1000121]

水平梯度

[−101−202−101]" role="presentation" style="position: relative;">⎡⎣⎢−1−2−1000121⎤⎦⎥[−101−202−101]

对上面的代码稍微修改算子即可

def SobelOperator(roi,operator_type):    if operator_type == "horizontal":        sobel_operator = np.array([[-1,-2,-1],[0,0,0],[1,2,1]])    elif operator_type == "vertical":        sobel_operator = np.array([[-1,0,1],[-2,0,2],[-1,0,1]])    else:        raise("type Error")    result = np.abs(np.sum(roi*sobel_operator))    return result    def SobelAlogrithm(image,operator_type):    new_image = np.zeros(image.shape)    image = cv2.copyMakeBorder(image,1,1,1,1,cv2.BORDER_DEFAULT)    for i in range(1,image.shape[0]-1):        for j in range(1,image.shape[1]-1):            new_image[i-1,j-1] = SobelOperator(image[i-1:i+2,j-1:j+2],operator_type)    new_image = new_image*(255/np.max(image))    return new_image.astype(np.uint8)
plt.subplot(121)plt.title("horizontal")plt.imshow(SobelAlogrithm(gray_saber,"horizontal"),cmap="binary")plt.axis("off")plt.subplot(122)plt.title("vertical")plt.imshow(SobelAlogrithm(gray_saber,"vertical"),cmap="binary")plt.axis("off")plt.show()

无论是Sobel还是Prewitt其实是求了单个方向颜色变化的极值

在某个方向上颜色的变化如图(此图来源于opencv官方文档)

对其求导可以得到这样一条曲线

由此我们可以得知边缘可以通过定位梯度值大于邻域的相素的方法找到(或者推广到大于一个阀值).
而Sobel就是利用了这一定理

Laplace算子

无论是Sobel还是Prewitt都是求单方向上颜色变化的一阶导(Sobel相比Prewitt增加了权重),那么如何才能反映空间上的颜色变化的一个极值呢?

我们试着在对边缘求一次导

在一阶导数的极值位置,二阶导数为0。所以我们也可以用这个特点来作为检测图像边缘的方法。 但是, 二阶导数的0值不仅仅出现在边缘(它们也可能出现在无意义的位置),但是我们可以过滤掉这些点。
在实际情况中我们不可能用0作为判断条件,而是设定一个阈值,这就引出来Laplace算子

[010 1−41 010 ]" role="presentation" style="position: relative;">[010 1−41 010 ][010 1−41 010 ]
[111 1−81 111 ]" role="presentation" style="position: relative;">[111 1−81 111 ][111 1−81 111 ]
def LaplaceOperator(roi,operator_type):    if operator_type == "fourfields":        laplace_operator = np.array([[0,1,0],[1,-4,1],[0,1,0]])    elif operator_type == "eightfields":        laplace_operator = np.array([[1,1,1],[1,-8,1],[1,1,1]])    else:        raise("type Error")    result = np.abs(np.sum(roi*laplace_operator))    return result    def LaplaceAlogrithm(image,operator_type):    new_image = np.zeros(image.shape)    image = cv2.copyMakeBorder(image,1,1,1,1,cv2.BORDER_DEFAULT)    for i in range(1,image.shape[0]-1):        for j in range(1,image.shape[1]-1):            new_image[i-1,j-1] = LaplaceOperator(image[i-1:i+2,j-1:j+2],operator_type)    new_image = new_image*(255/np.max(image))    return new_image.astype(np.uint8)
plt.subplot(121)plt.title("fourfields")plt.imshow(LaplaceAlogrithm(gray_saber,"fourfields"),cmap="binary")plt.axis("off")plt.subplot(122)plt.title("eightfields")plt.imshow(LaplaceAlogrithm(gray_saber,"eightfields"),cmap="binary")plt.axis("off")plt.show()

上图为比较取值不同的laplace算子实现的区别.

Canny算子

Canny边缘检测算子是一种多级检测算法。1986年由John F. Canny提出,同时提出了边缘检测的三大准则:

  • 低错误率的边缘检测:检测算法应该精确地找到图像中的尽可能多的边缘,尽可能的减少漏检和误检。
  • 最优定位:检测的边缘点应该精确地定位于边缘的中心。
  • 图像中的任意边缘应该只被标记一次,同时图像噪声不应产生伪边缘。

Canny边缘检测算法的实现较为复杂,主要分为以下步骤,

  1. 高斯模糊
  2. 计算梯度幅值和方向
  3. 非极大值 抑制
  4. 滞后阈值

高斯模糊

在前面的章节中已经实现过均值模糊,模糊在图像处理中经常用来去除一些无关的细节.

均值模糊引出了另一个思考,周边每个像素点的权重设为一样是否合适?
(周边像素点的权重是很多算法的区别,比如Sobel和Prewitt)

因此概率中非常熟悉的正态分布函数便被引入.正太分布函数的密度函数就是高斯函数,使用高斯函数来给周边像素点分配权重.

更详细的可以参考高斯模糊的算法

在opencv的官方文档中它介绍了这样一个5×5" role="presentation" style="position: relative;">5×55×5的高斯算子

相比直接的使用密度函数来分配权重,Opencv介绍的高斯算子是各像素点乘以一个大于1的比重,最后乘以一个1 \frac {159}" role="presentation" style="position: relative;">1 \frac {159}1 \frac {159},其实就是归一化的密度函数.这有效避免了计算机计算的精度问题

我们在此处直接使用3×3" role="presentation" style="position: relative;">3×33×3的算子

我们将其归一化然后代入上面的代码即可

def GaussianOperator(roi):    GaussianKernel = np.array([[1,2,1],[2,4,2],[1,2,1]])    result = np.sum(roi*GaussianKernel/16)    return resultdef GaussianSmooth(image):    new_image = np.zeros(image.shape)    image = cv2.copyMakeBorder(image,1,1,1,1,cv2.BORDER_DEFAULT)    for i in range(1,image.shape[0]-1):        for j in range(1,image.shape[1]-1):            new_image[i-1,j-1] =GaussianOperator(image[i-1:i+2,j-1:j+2])    return new_image.astype(np.uint8)
smooth_saber = GaussianSmooth(gray_saber)plt.subplot(121)plt.title("Origin Image")plt.axis("off")plt.imshow(gray_saber,cmap="gray")plt.subplot(122)plt.title("GaussianSmooth Image")plt.axis("off")plt.imshow(smooth_saber,cmap="gray")plt.show()

计算梯度幅值和方向

我们知道边缘实质是颜色变换的极值,可能出现在各个方向上,因此能否在计算时考虑多个方向的梯度,是算法效果优异的体现之一

在之前的Sobel等算子中我们将水平和垂直方向是分开考虑的.在Canny算子中利用下面公式将多个方向都考虑到

Gx = SobelAlogrithm(smooth_saber,"horizontal")Gy = SobelAlogrithm(smooth_saber,"vertical")
G = np.sqrt(np.square(Gx.astype(np.float64))+np.square(Gy.astype(np.float64)))cita = np.arctan2(Gy.astype(np.float64),Gx.astype(np.float64))

cita求出了图像每一个点的梯度,在之后的非极大值抑制中会有作用

plt.imshow(G.astype(np.uint8),cmap="gray")plt.axis("off")plt.show()

我们发现对于图像使用sobel算子使得获得的边缘较粗,下一步我们将使图像的线条变细

非最大值抑制

在获得梯度的方向和大小之后,应该对整幅图想做一个扫描,出去那些非边界上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。

(图像来源于opencv官方文档)

非极大值的算法实现非常简单

  • 比较当前点的梯度强度和正负梯度方向点的梯度强度。
  • 如果当前点的梯度强度和同方向的其他点的梯度强度相比较是最大,保留其值。否则抑制,即设为0。比如当前点的方向指向正上方90°方向,那它需要和垂直方向,它的正上方和正下方的像素比较。
def NonmaximumSuppression(image,cita):    keep = np.zeros(cita.shape)    cita = np.abs(cv2.copyMakeBorder(cita,1,1,1,1,cv2.BORDER_DEFAULT))    for i in range(1,cita.shape[0]-1):        for j in range(1,cita.shape[1]-1):            if cita[i][j]>cita[i-1][j] and cita[i][j]>cita[i+1][j]:                keep[i-1][j-1] = 1            elif cita[i][j]>cita[i][j+1] and cita[i][j]>cita[i][j-1]:                keep[i-1][j-1] = 1            elif cita[i][j]>cita[i+1][j+1] and cita[i][j]>cita[i-1][j-1]:                keep[i-1][j-1] = 1            elif cita[i][j]>cita[i-1][j+1] and cita[i][j]>cita[i+1][j-1]:                keep[i-1][j-1] = 1            else:                keep[i-1][j-1] = 0    return keep*image

这里的代码我们先创建了一个keep矩阵,凡是在cita中梯度大于周边的点设为1,否则设为0,然后与原图相乘.(这是我想得比较巧妙的一个方法)

nms_image = NonmaximumSuppression(G,cita)nms_image = (nms_image*(255/np.max(nms_image))).astype(np.uint8)
plt.imshow(nms_image,cmap="gray")plt.axis("off")plt.show()

在上面的极大值抑制中我们使边缘的线变得非常细,但是仍然有相当多的小点,得使用一种算法将他们去除

滞后阈值

滞后阈值的算法如下
Canny 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值):

  • 如果某一像素位置的幅值超过 高 阈值, 该像素被保留为边缘像素。
  • 如果某一像素位置的幅值小于 低 阈值, 该像素被排除。
  • 如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于 高 阈值的像素时被保留。

在opencv官方文档中推荐我们用2:1或3:1的高低阈值.

我们使用3:1的阈值来进行滞后阈值算法.

滞后阈值的算法可以分成两步首先使用最大阈值,最小阈值排除掉部分点.

MAXThreshold = np.max(nms_image)/4*3MINThreshold = np.max(nms_image)/4usemap = np.zeros(nms_image.shape)high_list = []
for i in range(nms_image.shape[0]):    for j in range(nms_image.shape[1]):        if nms_image[i,j]>MAXThreshold:            high_list.append((i,j))
direct = [(0,1),(1,1),(-1,1),(-1,-1),(1,-1),(1,0),(-1,0),(0,-1)]def DFS(stepmap,start):    route = [start]    while route:        now = route.pop()        if usemap[now] == 1:            break        usemap[now] = 1        for dic in direct:            next_coodinate = (now[0]+dic[0],now[1]+dic[1])            if not usemap[next_coodinate] and nms_image[next_coodinate]>MINThreshold             and next_coodinate[0]<stepmap.shape[0]-1 and next_coodinate[0]>=0             and next_coodinate[1]<stepmap.shape[1]-1 and next_coodinate[1]>=0:                route.append(next_coodinate)
for i in high_list:    DFS(nms_image,i)
plt.imshow(nms_image*usemap,cmap="gray")plt.axis("off")plt.show()

可以发现这次的效果并不好,这是由于最大最小阈值设定不是很好导致的,我们将上面代码整理,将最大阈值,最小阈值改为可变参数,调参来优化效果.

Canny边缘检测代码

def CannyAlogrithm(image,MINThreshold,MAXThreshold):    image = GaussianSmooth(image)    Gx = SobelAlogrithm(image,"horizontal")    Gy = SobelAlogrithm(image,"vertical")    G = np.sqrt(np.square(Gx.astype(np.float64))+np.square(Gy.astype(np.float64)))    G = G*(255/np.max(G)).astype(np.uint8)    cita = np.arctan2(Gy.astype(np.float64),Gx.astype(np.float64))    nms_image = NonmaximumSuppression(G,cita)    nms_image = (nms_image*(255/np.max(nms_image))).astype(np.uint8)    usemap = np.zeros(nms_image.shape)    high_list = []    for i in range(nms_image.shape[0]):        for j in range(nms_image.shape[1]):            if nms_image[i,j]>MAXThreshold:                high_list.append((i,j))                    direct = [(0,1),(1,1),(-1,1),(-1,-1),(1,-1),(1,0),(-1,0),(0,-1)]    def DFS(stepmap,start):        route = [start]        while route:            now = route.pop()            if usemap[now] == 1:                break            usemap[now] = 1            for dic in direct:                next_coodinate = (now[0]+dic[0],now[1]+dic[1])                if next_coodinate[0]<stepmap.shape[0]-1 and next_coodinate[0]>=0                 and next_coodinate[1]<stepmap.shape[1]-1 and next_coodinate[1]>=0                 and not usemap[next_coodinate] and nms_image[next_coodinate]>MINThreshold:                    route.append(next_coodinate)    for i in high_list:        DFS(nms_image,i)    return nms_image*usemap

我们试着调整阈值,来对其他图片进行检测

通过调整阈值可以减少毛点的存在,控制线的清晰

(PS:在使用canny过程中为了减少python运算的时间,我将图片进行了缩放,这也导致了效果不够明显,canny边缘检测在高分辨率情况下效果会更好)

参考资料

Opencv文档
stackoverflow
Numpy设计的是如此出色,以至于在实现算法时我能够忽略如此之多无关的细节

(0)

相关推荐

  • 基于阈值的车道标记

    重磅干货,第一时间送达 在这篇文章中,我将介绍如何从视频中查找并标记车道.被标记的车道会显示到视频上,并得到当前路面的曲率以及车辆在该车道内的位置.首先我们需要对图像进行相机失真校正,这里就不作详细介 ...

  • 基于OpenCV的实用图像处理操作

    重磅干货,第一时间送达 图像处理适用于图像和视频.良好的图像处理结果会为后续的进一步处理带来很大的帮助,例如提取到图像中的直线有助于对图像中物体的结构进行分析,良好的特征提取会优化深度学习的结果等.今 ...

  • 使用OpenCV实现图像增强

    重磅干货,第一时间送达 本期将介绍如何通过图像处理从低分辨率/模糊/低对比度的图像中提取有用信息. 下面让我们一起来探究这个过程: 首先我们获取了一个LPG气瓶图像,该图像取自在传送带上运行的仓库.我 ...

  • 稀疏优化算法综述

    转载自 http://cea.ceaj.org/CN/abstract/abstract31975.shtml 摘 要:压缩感知是近年来发展起来的关于信息获取与处理的全新理论框架.主要介绍压缩感知的基 ...

  • 一个简单方法识别毛玻璃、高斯模糊

    作者:晟沚 前  言 本文主要推荐一种简单的方法识别带有毛玻璃.高斯模糊等效果的图片. 01 毛玻璃效果 毛玻璃效果的原理,即遍历每一个像素,随机选取这个像素周围的某一个像素,替换当前像素.可以使用o ...

  • 图像处理知多少?准大厂算法工程师30+场秋招后总结的面经问题详解

    作者丨灯会 来源丨极市平台 编辑丨极市平台 极市导读 本篇主要包含了图像滤波.边缘检测相关常考内容等相关面试经验. >>加入极市CV技术交流群,走在计算机视觉的最前沿 系列文章: 深度学习 ...

  • python+opencv图像处理(三十九)

    MSR 又一学期开学季,感觉好久没说过话了,多说一会儿这个嗓子有点儿受不了了,老天啊,救救我吧...或许,我应该去学一门说话艺术之类的课? 之前简单写过Retinex理论以及单尺度SSR. 今天要写的 ...

  • python+opencv图像处理(三十八)

    Retinex理论 最近研究了下Retinex,这个算法用的地方还挺多的,较多是用在水下图像或者去雾等,还没有研究得太清楚,先做个笔记 . Retinex是Edwin.H.Land于1963年提出的建 ...

  • python+opencv图像处理(三十七)

    梯形高通滤波和指数高通滤波 大人的世界很精彩... 大人的世界很无奈... 大人的心思很复杂... 大人的心思猜不透... 1.梯形高通滤波 其传递函数如下: 式中,D0为截止频率,D1可以任取,但要 ...

  • python+opencv图像处理(三十六)

    理想和巴特沃斯高通滤波 大自然的鬼斧神工...... 低通滤波器是滤除高频分量,保留低频分量,主要是用于平滑图像,去除图像噪声的. 高通滤波器与之相对应,是滤除低频分量,保留高频分量,主要用来锐化边缘 ...

  • python+opencv图像处理(三十五)

    指数低通滤波器 最近好多事情,一件接一件,何时是个头,何时才能休... 1.指数低通滤波 它的传递函数如下:     H(u,v)=     式中,D0为截止频率,     D(u,v)也是从频率域的 ...

  • python+opencv图像处理(三十四)

    梯形低通滤波器 梯形低通滤波器类似上篇巴特沃斯低通滤波,只是其转移函数不同. 梯形低通滤波器的转移函数如下: 式中,D0为截止频率,D1可以任取,但要求D1>D0. 上两个都不能为负. D(u, ...

  • python+opencv图像处理(三十二)

    理想低通滤波 淅淅沥沥的雨天...... 天晴想下雨,下雨想天晴... 理想低通滤波是滤掉高频部分,仅允许低频通过,以去掉噪声,使图像得到平滑. 其实现步骤: 1. 将图像进行傅里叶变换,将低频移至中 ...

  • python+opencv图像处理(三十一)

    傅里叶变换 对图像的傅里叶变换,就是将图像从图像空间变换到频率空间,从而可以在频率域对图像进行处理. 1.傅里叶变换 傅里叶变换的理论在任何一本数字图像处理的书里都有,网上也有很多,这里就不再解释了, ...

  • python+opencv图像处理(三十)

    双边滤波 双边滤波是一种非线性的滤波方法,能够保持边界清晰的情况下有效的去除噪声,它拥有类似相机里美颜的效果. 双边滤波之所以能够做到保边去噪的效果,是由于它的有两个核:空间域核和值域核,比高斯滤波只 ...