基于Python的OpenCV图像处理9

May 22,2017   2991 words   11 min


一、动态调整效果

在之前我们学习了利用窗口对一幅图像进行展示,但有时我们计算了一个阈值,想通过滑动进度条的方式 改变阈值,从而观察不同阈值对处理效果的影响。有时需要观察处理前后的差别,这些都可以用进度条完成。 这就是本部分要说的内容。在OpenCV中已经非常人性化地内置了这种进度条,我们不用去管内部实现原理, 只需要简单调用即可。cv2.createTrackbar()用于创建一个进度条,cv2.getTrackbarPos()用于获取一个进度条。 代码如下:

# coding=utf-8
import numpy as np
import cv2


# 定义滑动条的回调函数,这里什么也不做,直接pass即可。这里的x便是传递回来的滑动条的位置。我们可以print到控制台中。
def nothing(x):
    pass


# 首先读取一个图像用于处理
img = cv2.imread("E:\\bin.jpg")
# 将图像转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 一些变量的初始化
threshold = 128
result = gray

# 新建一个窗口,并命名为Threshold Test
cv2.namedWindow("Threshold Test")

# 创建两个进度条,分别用于控制阈值大小和效果开关
# 第一个参数是进度条的名字,也是识别进度条的唯一ID
# 第二个参数是需要绑定进度条的窗体的名字
# 第三个参数是进度条的初始值
# 第四个参数是进度条的最大值
# 第五个参数是回调函数,每次滑动都会调用回调函数。通常包含一个默认参数,即滑动条的位置。在这里我们不做任何事情直接定义一个函数然后pass即可
cv2.createTrackbar('Threshold', 'Threshold Test', threshold, 255, nothing)

# 进度条的第二个作用便是当Switch开关。其赋值只有0和1。
cv2.createTrackbar('0:Off 1:On', 'Threshold Test', 1, 1, nothing)

# 循环执行语句
while 1:
    # 设置显示的图片,由于之前对result赋过值,这里显示的是灰度图像
    cv2.imshow('Threshold Test', result)

    # 实现Esc退出,因为这是一个死循环,所以必须要有退出代码。如果点击窗体上的×是关不了的。
    k = cv2.waitKey(1) & 0xff
    if k == 27:
        break

    # 获取当前进度条的状态,返回值是进度条当前位置
    # 第一个参数是要获取的进度条的ID
    # 第二个参数是窗体的名称
    threshold = cv2.getTrackbarPos('Threshold', 'Threshold Test')
    s = cv2.getTrackbarPos('0:Off 1:On', 'Threshold Test')

    # 对是否开启效果进行判断
    if s == 0:
        # 如果没有开启,那么直接显示读取的灰度图像
        result = gray
    else:
        # 如果开启效果,那么调用二值化函数,并将Threshold进度条的位置传递给它,实现动态变化
        ret, result = cv2.threshold(gray, threshold, 255, cv2.THRESH_BINARY)

# 最后完成之后别忘销毁窗口
cv2.destroyAllWindows()

实现的效果如下:

二、Canny边缘检测

1.噪声去除

由于边缘检测很容易受到噪声影响,因此 第一步使用5×5的高斯滤波器去除噪声。

2.计算图像梯度

对平滑后的图像利用Sobel算子计算水平方向和竖直方向的一阶导数(图像梯度,Gx和Gy)。根据得到的这两幅梯度图(Gx和Gy) 找到边界的梯度和方向。计算梯度与方向的公式与之前相同,这里就不再赘述。梯度的方向一般与边界垂直,其一般被归为 4类:垂直、水平、和两个对角线方向。

3.非极大值抑制

得到梯度方向和大小后,应该对图像做一个整体扫描,去除那些非边界上的点。具体做法是对每一个像素进行检查,看这个点 的梯度是不是周围具有相同梯度方向的点中最大的。 如图所示在同一梯度方向上有A、B、C三个点。其中A位于边界上,而B、C不在边界上。所以A的梯度要大于B、C。因此要去除B、C, 保留A。会得到一个包含“窄边界”的图像。

4.滞后阈值

现在需要确定哪些边界是真正的边界。需要设定两个阈值:minVal、maxVal。当图像的灰度梯度高于maxVal时,认为是真正的边界, 小于minVal时认为不是边界。介于两者之间时,若其与某个被确定为真正边界的点相连,则认为它是边界点,否则不是。如下图: 图中A高于maxVal,因此认为是真边界。C虽然低于maxVal,但与A相连且大于minVal,所以也认为是真边界。而B虽介于两者之间, 但不与真边界相连,所以不认为是边界。在这一步一些小的噪声点也会被去除,因为我们认为边界都是一些长的线段。

5.OpenCV实现

在OpenCV中,只需要cv2.Canny()函数便可以实现以上步骤,且无需关心原理。实现代码如下:

# coding=utf-8
import numpy as np
import cv2


def nothing(x):
    pass


img = cv2.imread("E:\\bin.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

minVal = 100
maxVal = 200
switch = 1
result = gray

cv2.namedWindow("Canny Test")

cv2.createTrackbar('minVal', 'Canny Test', minVal, 255, nothing)
cv2.createTrackbar('maxVal', 'Canny Test', maxVal, 255, nothing)
cv2.createTrackbar('0:Off 1:On', 'Canny Test', switch, 1, nothing)

while 1:
    cv2.imshow('Canny Test', result)

    k = cv2.waitKey(1) & 0xff
    if k == 27:
        break

    minVal = cv2.getTrackbarPos('minVal', 'Canny Test')
    maxVal = cv2.getTrackbarPos('maxVal', 'Canny Test')
    switch = cv2.getTrackbarPos('0:Off 1:On', 'Canny Test')

    if switch == 1:
        # 第一个参数是待处理的图像
        # 第二个参数是最小阈值minVal
        # 第三个参数是最大阈值maxVal
        # 第四个参数是Sobel算子卷积核大小,默认为3,可省略
        # 第五个参数是L2Gradient,用于设定求梯度大小的方程。如果为True,则用开根号的那个,否则用绝对值的那个,默认为False,可省略
        result = cv2.Canny(gray, minVal, maxVal)
    else:
        result = gray

cv2.destroyAllWindows()

效果如下: 这里初始设置的阈值分别是1000,200。还可以拖动进度条改变阈值的大小观察不同效果:

本文作者原创,未经许可不得转载,谢谢配合

返回顶部