【译】手把手教你使用图像处理利器OpenCV

做者:Muhammad Junaid Khalidpython

翻译:老齐算法

与本文相关的图书推荐:《数据准备和特征工程》编程


概要

在本文中,将学习如何使用Python语言进行图像处理,咱们不会局限于一个单独的库或框架,然而,有一个库的使用率将会是最高的,那就是OpenCV。咱们一开始会讨论一些图像处理,而后继续探讨不一样的应用/场景,也就是图像处理的用武之地。开始吧!bash

什么是图像处理?

在深刻研究图像处理的方法以前,重要的是要了解什么是图像处理,特别是这项技术在处理大量图片方面的角色。图像处理完整的说法是“数字图像处理”,常用图像处理的领域是“计算机视觉”。对这两个术语不要混淆,图像处理算法和计算机视觉(CV)算法都以图像为输入,然而,在图像处理中,输出也是图像,而在计算机视觉中,输出能够是关于图像的一些特征或信息。微信

为何须要图像处理?

咱们收集或生成的数据大部分是原始数据,也就是说,因为一些可能的缘由,这些数据不适合直接用于应用程序。所以,咱们须要首先分析它,执行必要的预处理,而后使用它——特别推荐《数据准备和特征工程》,此书即为这方面最佳读物。markdown

例如,咱们正在尝试构建一个关于猫的分类器。咱们的程序会把一个图像做为输入,而后告诉咱们这个图像是否包含一只猫。构建这个分类器的第一步是收集数百张含有猫的图片。一个常见的问题是,收集的全部图片的大小都不相同,所以在将它们提供给模型进行训练以前,须要调整它们的大小或者把它们进行预处理,使尺寸符合标准。框架

为何图像处理对于任何计算机视觉应用序都是必不可少的?以上提到的只是众多缘由之一。dom

预备知识

为了轻松地学习本文内容,你须要已经具有以下知识。机器学习

首先,应该具有必定的编程语言技能,本文使用的是Python语言,若是还没有掌握此语言,推荐阅读《跟老齐学Python:轻松入门》或《Python大学实用教程》。编程语言

其次,你应该了解什么是机器学习以及它的基本工做原理。由于在本文中咱们将使用一些机器学习算法来进行图像处理。

另外,若是你以前接触过或掌握了OpenCV的基本知识,也会有所帮助。但这不是必需的。

还有,你必定要了解图像在内存中到底是如何表示的。每幅图像都由一组像素表示,即像素值矩阵。对于灰度图像,像素值的范围是0到255,它们表示该像素的强度。例如,若是你有一个20×20维的图像,它将由一个20x20的矩阵表示(像素值总共是400)。

若是你正在处理彩色图像,你应该知道它有三个通道——红、绿、蓝(RGB)。所以,一个彩色图像有三个这样的矩阵。

安装

注意: 因为咱们将经过Python使用OpenCV,因此你必须会实用它,前面推荐了关于Python的书籍。下面依次说明在不一样操做系统中OpenCV的安装方法:

  • Windows
$ pip install opencv-python
复制代码
  • MacOS
$ brew install opencv3 --with-contrib --with-python3
复制代码
  • Linux
$ sudo apt-get install libopencv-dev python-opencv
复制代码

要检查是否安装成功,请在Python交互模式中运行如下命令:

import cv2
复制代码

必备的基础知识

在进行图像处理以前,要先作一些准备。

在本文中,咱们将使用如下图像:

注意: 为了在本文中显示该图像,对其进行了缩放,可是咱们使用的图像原始大小约为1180x786。

你可能注意到图像如今是彩色的,这意味着它由三个颜色通道表示,即红色、绿色和蓝色。咱们将把图像转换成灰度,并使用下面的代码将图像分割成单独的通道。

找到图像细节

使用imread()函数加载图像后,咱们能够获得关于它的一些简单属性,好比像素的数量和尺寸:

import cv2

img = cv2.imread('rose.jpg')

print("Image Properties")
print("- Number of Pixels: " + str(img.size))
print("- Shape/Dimensions: " + str(img.shape))
复制代码

Output:

Image Properties
- Number of Pixels: 2782440
- Shape/Dimensions: (1180, 786, 3)
复制代码

将图像分割成单独的通道

如今,咱们将使用OpenCV将图像分割成红色、绿色和蓝色的部分,并显示它们:

from google.colab.patches import cv2_imshow

blue, green, red = cv2.split(img)    # Split the image into its channels
img_gs = cv2.imread('rose.jpg', cv2.IMREAD_GRAYSCALE)    # Convert image to grayscale

cv2_imshow(red) # Display the red channel in the image
cv2_imshow(blue) # Display the red channel in the image
cv2_imshow(green) # Display the red channel in the image
cv2_imshow(img_gs) # Display the grayscale version of image
复制代码

为了简单起见,咱们只显示灰度图像。

图像阈值

阈值的概念很是简单。正如上面在图像表示中所讨论的,像素值能够是0到255之间的任何值。假设咱们想要将一幅图像转二值化,即指定一个像素值为0或1。为此,咱们能够设置阈值。例如,若是阈值(T)为125,那么全部大于125的像素将被赋值为1,全部小于或等于该值的像素将被赋值为0。下面,咱们经过代码来更好地理解它。

将下面的图像用上述方法进行转换:

import cv2

# Read image
img = cv2.imread('image.png', 0)

# Perform binary thresholding on the image with T = 125
r, threshold = cv2.threshold(img, 125, 255, cv2.THRESH_BINARY)
cv2_imshow(threshold)
复制代码

输出:

正如你所看到的, 二值化以后,出现了两个区域,即黑色区域(像素值0)和白色区域(像素值1)。原来, 咱们设置的阈值正好在图像的中间,这就是为何黑白值在那里被分割。

应用

1:去除图像中的噪声

如今你已经对图像处理的概念和用途有了基本的了解,接下来让咱们来了解一下它的一些具体应用。

在大多数状况下,咱们收集的原始数据有噪声,也就是说,不须要的特征使图像很难被感知。虽然这些图像能够直接用于特征抽取,可是算法的准确性会受到很大的影响。这就是为何在将图像传递给算法以得到更好的精度以前,要对图像进行处理的缘由。

有许多不一样类型的噪声,如高斯噪声,椒盐噪声等。咱们能够经过应用滤波器来去除图像中的噪声,或者至少将其影响降到最低。在滤波器方面也有不少选择,每个滤波器都有不一样的优势。所以,对于特定类型的噪声来讲,总有一个是最好的。

为了更好地理解这一点,咱们将在上面的玫瑰色图像的灰度版本中添加“盐和胡椒粉”噪声,而后尝试使用不一样的滤波器去除图像中的噪声,看看哪个最适合这种类型。

import numpy as np

# Adding salt & pepper noise to an image

def salt_pepper(prob):
      # Extract image dimensions
      row, col = img_gs.shape

      # Declare salt & pepper noise ratio
      s_vs_p = 0.5
      output = np.copy(img_gs)

      # Apply salt noise on each pixel individually
      num_salt = np.ceil(prob * img_gs.size * s_vs_p)
      coords = [np.random.randint(0, i - 1, int(num_salt))
            for i in img_gs.shape]
      output[coords] = 1

      # Apply pepper noise on each pixel individually
      num_pepper = np.ceil(prob * img_gs.size * (1. - s_vs_p))
      coords = [np.random.randint(0, i - 1, int(num_pepper))
            for i in img_gs.shape]
      output[coords] = 0
      cv2_imshow(output)

      return output

# Call salt & pepper function with probability = 0.5
# on the grayscale image of rose
sp_05 = salt_pepper(0.5)

# Store the resultant image as 'sp_05.jpg'
cv2.imwrite('sp_05.jpg', sp_05)
复制代码

好的,咱们已经把噪声添加到玫瑰图像,这是它如今的样子:

让咱们如今应用不一样的滤波器,并记下观察结果,即每一个滤波器降噪的效果。

锐化滤波器

# Create our sharpening kernel, the sum of all values must equal to one for uniformity
kernel_sharpening = np.array([[-1,-1,-1],
                              [-1, 9,-1],
                              [-1,-1,-1]])

# Applying the sharpening kernel to the grayscale image & displaying it.
print("\n\n--- Effects on S&P Noise Image with Probability 0.5 ---\n\n")

# Applying filter on image with salt & pepper noise
sharpened_img = cv2.filter2D(sp_05, -1, kernel_sharpening)
cv2_imshow(sharpened_img)
复制代码

在有椒盐噪声的图像上应用滤波器获得的图像以下所示。经过与原始灰度图的对比,咱们能够看出,它把图像调得太亮了,也没法突出玫瑰上的亮点。所以,咱们能够得出结论,锐化滤波器并不能去除噪声。

锐化滤波器输出:

中值滤波器

from scipy.ndimage import maximum_filter, minimum_filter

def midpoint(img):
    maxf = maximum_filter(img, (3, 3))
    minf = minimum_filter(img, (3, 3))
    midpoint = (maxf + minf) / 2
    cv2_imshow(midpoint)

print("\n\n---Effects on S&P Noise Image with Probability 0.5---\n\n")
midpoint(sp_05)
复制代码

在有噪声的图像上应用中值滤波器,获得的图像以下所示。经过与原始灰度图像的对比,咱们能够看出,与上面的核方法同样,图像的亮度调高了不少,然而,它可以突出玫瑰上的亮斑(即噪声)。所以,咱们能够说,中值滤波器是比锐化滤波器更好的选择,但它仍然不能彻底恢复原始图像。

中值滤波器输出:

逆谐波均值滤波器

注意: 对这些滤波器的工做原理的阐述,超出了本文范畴,读者能够在网上搜索,咱们仍是从应用的层面来研究。

def contraharmonic_mean(img, size, Q):
    num = np.power(img, Q + 1)
    denom = np.power(img, Q)
    kernel = np.full(size, 1.0)
    result = cv2.filter2D(num, -1, kernel) / cv2.filter2D(denom, -1, kernel)
    return result

print("\n\n--- Effects on S&P Noise Image with Probability 0.5 ---\n\n")
cv2_imshow(contraharmonic_mean(sp_05, (3,3), 0.5))
复制代码

在有椒盐噪声的图像上应用逆谐波均值滤波器(en.wikipedia.org/wiki/Contra…)获得的图像以下图所示。经过与原始灰度图像的对比,咱们能够看到:它几乎完美再现了原始图像。它的强度或亮度级别与原图是相同的,它突出了玫瑰上的亮点。所以,咱们能够得出结论,逆谐波均值滤波器在处理椒盐噪声方面是很是有效的。

逆谐波均值滤波器输出:

如今咱们已经找到了最佳滤波器,它能够有效地把有噪声的图像恢复到原始图像。咱们能够继续下一个应用了。

2:使用Canny算子进行边缘检测

到目前为止,咱们使用的玫瑰图像的背景是不变的,也就是黑色的,所以,咱们将把这个应用用于不一样的图像,以更好地展现算法的功效。缘由是,若是背景是恒定的,边缘检测任务就变得至关简单,这不是咱们所但愿的。

在本文开始部分,咱们提到了一个关于猫的分类器。如今咱们延用这个例子,看看图像处理如何在其中扮演一个完整的角色。

在分类算法中,首先扫描图像寻找“对象”。也就是说,当你输入一幅图像时,算法会找到图像中的全部对象,而后将它们与你试图寻找的对象进行特征比较。对于猫分类器,它会将在图像中找到的全部对象与猫图像的特征进行比较,若是找到匹配项,它会告诉咱们输入图像中包含了一只猫。

对于这个猫分类器,仅以一张猫的图像为例,如下是咱们将要使用的图像:

import cv2
import numpy as np
from matplotlib import pyplot as plt

# Declaring the output graph's size
plt.figure(figsize=(16, 16))

# Convert image to grayscale
img_gs = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imwrite('gs.jpg', img_gs)

# Apply canny edge detector algorithm on the image to find edges
edges = cv2.Canny(img_gs, 100,200)

# Plot the original image against the edges
plt.subplot(121), plt.imshow(img_gs)
plt.title('Original Gray Scale Image')
plt.subplot(122), plt.imshow(edges)
plt.title('Edge Image')

# Display the two images
plt.show()
复制代码

边缘检测输出:

正如你所看到的,图像中包含对象的部分(在本例中是一只猫)已经经过边缘检测用虚线标出或分隔开。如今你必定想知道,什么是边缘检测的Canny算子,它是怎么工做的?如今就讨论一下。

要理解上述内容,须要讨论三个关键步骤。首先,它对图像进行降噪,降噪方式与前面讨论的方式相似。其次,它使用每一个像素的一阶导数来找到边缘。这背后的逻辑是,在边缘存在的地方,会有一个忽然的强度变化,致使一阶导数值达到峰值,从而使该像素成为“边缘像素”。

最后,进行滞后阈值化;上面咱们说过,在一个边缘的一阶导数值会有一个峰值,可是咱们没有说:这个峰值须要有多高,才能被归类为一个边缘——这叫作阈值!在本文的前面,咱们讨论了什么是简单的阈值。迟滞阈值法是在此基础上的一种改进,它利用两个阈值来代替一个阈值。这背后的缘由是,若是阈值太高,咱们可能会错过一些真正的边缘(真负例),若是阈值太低,咱们会获得不少被归类为边缘的点,而实际上不是边缘(假正例)。一个阈值设置为高,一个设置为低,将全部高于“高阈值”的点标识为边缘,而后对全部高于“低阈值”但低于“高阈值”的点进行评估;边缘上的点肯定以后,与边缘点靠近或相邻的点也被肯定为边缘,其他的点被丢弃。

这些是Canny算子用于识别图像边缘的基本概念/方法。

译者注: Canny算子是澳洲计算机科学家约翰·坎尼(John F. Canny)于1986年开发出来的一个多级边缘检测算法,其目标是找到一个最优的边缘.

结论

在本文中,咱们学习了如何在不一样的平台(如Windows、MacOS和Linux)上安装OpenCV,以及如何验证安装成功。OpenCV是Python中最流行的图像处理库。

接着咱们讨论了什么是图像处理,以及它在机器学习的计算机视觉领域中的应用。咱们讨论了一些常见的噪声类型,以及如何使用不一样的滤波器将噪声从图像中去除,以便在应用中使用这些图像。

此外,咱们还了解了图像处理如何在高端应用(如:对象检测或分类)中发挥不可或缺的做用。请注意,这篇文章只是冰山一角,数字图像处理还有更多的内容,不可能在一篇短文中所有涵盖。请关注微信公众号「老齐教室」,这里还会刊发有关图像处理的文章。

原文连接:stackabuse.com/introductio…

搜索技术问答的公众号:老齐教室

在公众号中回复:老齐,可查看全部文章、书籍、课程。

相关文章
相关标签/搜索