使用Python+OpenCV进行图像处理(二)

磐创AI

    
    图像预处理对于整个图像处理任务来讲特别重要。如果我们没有进行恰当的预处理,无论我们有多么好的数据也很难得到理想的结果。
    本篇是视觉入门系列教程的第二篇。整个视觉入门系列内容如下:
    理解颜色模型与在图像上绘制图形(图像处理基本操作)。
    基本的图像处理与滤波技术。
    从特征检测到人脸检测。
    图像分割与分水岭(Watershed)算法(TBU)
    在边缘和轮廓检测中,噪声对检测的精度有很大的影响。因此,去除噪声和控制像素值的大小可以帮助模型聚焦于整体特征,获得更高的精度。对应的图像处理技术包括:模糊化(Blurring)、阈值化(thresholding)和形态转换(morphological transformation)。本篇我们将详细介绍这几个常见的图像预处理技术。(本文假设读者已经熟悉卷积的概念。)
    模糊化(Blurring)
    模糊化的目标是实现降噪。我们必须格外注意的是:如果我们把边缘检测算法应用到高分辨率的图像上,我们就会得到很多我们不感兴趣的检测结果;
    
    相反,如果我们把图像模糊太多,我们就会丢失数据。因此,我们需要找到一个适当的模糊量,从而不失去理想的边缘。
    有多种技术用于实现模糊效果,在这里我们讨论OpenCV中常用的四种技术:平均模糊(Averaging blurring)、高斯模糊(Gaussian blurring)、中值模糊(median blurring)和双边滤波(bilateral filtering)。这四种技术应用一个共同的基本原理,即使用滤波器(内核)对图像进行卷积运算。不同的是,在四种模糊方法中使用的滤波器的值是不同的。
    平均模糊(Average blurring)是取给定内核(kernel)区域下所有像素值的平均值替换中心的值。例如,假设给定一个大小为5X5的内核(kernel),我们计算卷积结果的平均值,并将结果放在给定区域的中心。示例如下:
    
    如果我们增加内核的大小,像素值将更加归一化。因此图像也会变得越来越模糊。让我们用下面的代码对比处理结果。(为了便于比较,将把原始图像加到结果中,进行对比显示。)
    # Import the image and convert to RGB
    img = cv2.imread('text.jpg')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    # Plot the image with different kernel sizes
    kernels = [5, 11, 17]
    fig, axs = plt.subplots(nrows = 1, ncols = 3, figsize = (20, 20))
    for ind, s in enumerate(kernels):
    img_blurred = cv2.blur(img, ksize = (s, s))
    ax = axs[ind]
    ax.imshow(img_blurred)
    ax.axis('off')
    plt.show()
    
    中值模糊(Medium blurring)和平均模糊(Average blurring)是一样的,只是它使用的是中值而不是平均值。正由于这个特性,当我们需要处理图像中突然出现的噪音时(如“椒盐噪音”),使用中值模糊(medium blurring)的效果要比平均模糊(average blurring)效果好。
    
    高斯模糊(Gaussian blurring)是使用“值”具有高斯分布的核函数。由于这些值是由高斯函数生成的,因此它的参数需要一个sigma值。如上图,内核的值在靠近中心的地方变高,在靠近角的地方变小。将该方法应用于具有正态分布的噪声,如白噪声,效果较好。
    双边滤波(Bilateral Filtering)是高斯模糊的一个高级版本。模糊化不仅可以溶解噪声,而且还会平滑边缘。而双边滤波器能在去除噪声的同时保持边缘锐化。这是由于它不仅使用高斯分布值,还同时考虑了距离和像素值的差异。因此,需要指定sigmaSpace和sigmaColor这两个参数。
    # Blur the image
    img_0 = cv2.blur(img, ksize = (7, 7))
    img_1 = cv2.GaussianBlur(img, ksize = (7, 7), sigmaX = 0)
    img_2 = cv2.medianBlur(img, 7)
    img_3 = cv2.bilateralFilter(img, 7, sigmaSpace = 75, sigmaColor =75)
    # Plot the images
    images = [img_0, img_1, img_2, img_3]
    fig, axs = plt.subplots(nrows = 1, ncols = 4, figsize = (20, 20))
    for ind, p in enumerate(images):
    ax = axs[ind]
    ax.imshow(p)
    ax.axis('off')
    plt.show()
    
    阈值化(Thresholding)
    图像的阈值化就是利用图像像素点分布规律,设定阈值进行像素点分割,进而得到图像的二值图像。我们需要设置阈值和最大值,然后据此相应地进行像素值转换。常用的阈值化包含有五种不同的类型:二进制阈值化、反二进制阈值化、阈值化到零、反阈值化到零,和阈值截断。
    img = cv2.imread('gradation.png')
    # Thresholding
    _, thresh_0 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
    _, thresh_1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
    _, thresh_2 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
    _, thresh_3 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
    _, thresh_4 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
    # Plot the images
    images = [img, thresh_0, thresh_1, thresh_2, thresh_3, thresh_4]
    fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (13, 13))
    for ind, p in enumerate(images):
    ax = axs[ind//3, ind%3]
    ax.imshow(p)
    plt.show()
    
    
    如上图所示,每种类型的阈值都可以用数学公式表示,I(x, y)是像素点的强度(也称为点(x, y)的像素值)。上图中的图像示例,可以更直观的理解不同阈值化类型之间的区别。
    只取一个阈值并将其应用于图像的所有部分并不能满足我们的全部需求。如果我们有一张在多个不同区域亮度差异较多的图片这种情况,将一个值应用于整个图像一般不利于我们的图像处理任务。其对应更好的方法是对图像的每个部分使用不同的阈值。对应这种情况还有另外一种阈值化技术称为自适应阈值化(Adaptive threshilding)。通过对图像邻域内阈值的计算,可以得到不同光照条件下的较好结果。
    # Convert the image to grayscale
    img = cv2.imread('text.jpg')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Adaptive Thresholding
    _, thresh_binary = cv2.threshold(img, thresh = 127, maxval = 255, type = cv2.THRESH_BINARY)
    adap_mean_2 = cv2.adaptiveThreshold(img, 255,
    cv2.ADAPTIVE_THRESH_MEAN_C,
    cv2.THRESH_BINARY, 7, 2)
    adap_mean_2_inv = cv2.adaptiveThreshold(img, 255,
    cv2.ADAPTIVE_THRESH_MEAN_C,
    cv2.THRESH_BINARY_INV, 7, 2)
    adap_mean_8 = cv2.adaptiveThreshold(img, 255,
    cv2.ADAPTIVE_THRESH_MEAN_C,
    cv2.THRESH_BINARY, 7, 8)
    adap_gaussian_8 = cv2.adaptiveThreshold(img, 255,
    cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv2.THRESH_BINARY, 7, 8)
    我们需要将颜色模式转换为灰度来进行自适应阈值化。自适应阈值的参数有maxValue(在上面的示例中设置为255)、adaptiveMethod、thresholdType、blocksize和C。这里使用的自适应方法有两种:adaptivethresholdmeanc和adaptivethresholdgaussianc。让我们通过下方代码对比自适应阈值化的不同结果。
    # Plot the images
    images = [img, thresh_binary, adap_mean_2, adap_mean_2_inv,
    adap_mean_8, adap_gaussian_8]
    fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (15, 15))
    for ind, p in enumerate(images):
    ax = axs[ind%2, ind//2]
    ax.imshow(p, cmap = 'gray')
    ax.axis('off')
    plt.show()
    
    如上图所示,左边为原始图像与二进制阈值化结果图。对比二进制阈值化结果图与右上方两张结果图(由adaptivethresholdmeanc方法生成)可得,后者生成了更为详细的结果。我们还可以看出,当C值更大时,图像将变得更显式。C代表从均值或加权均值中减去值的大小。通过观察上图右子图上下两幅图像,我们还可以对比查看相同C值下adaptivethreshold meanc和adaptivethreshold _gaussianc两种方法生成的不同效果图。
    
    
    1  2  下一页>