📜  使用OpenCV在Python中进行图像处理

📅  最后修改于: 2020-08-25 07:42:26             🧑  作者: Mango

介绍

在本教程中,我们将学习如何使用Python语言执行图像处理。我们不会将自己局限于单个库或框架;但是,我们将最常使用的是Open CV库。我们将先讨论一些图像处理,然后再继续介绍可以方便使用图像处理的不同应用程序/场景。所以,让我们开始吧!

什么是图像处理?

重要的是要了解图像处理的确切含义以及在更大范围内图像处理的作用,然后再研究其处理方式。图像处理最常被称为“数字图像处理”,而经常使用的领域是“计算机视觉”。请勿混淆-我们将讨论这两个术语以及它们如何连接。图像处理算法和计算机视觉(CV)算法都将图像作为输入。但是,在图像处理中,输出也是图像,而在计算机视觉中,输出可能是有关图像的某些特征/信息

我们为什么需要它?

我们收集或生成的数据大部分是原始数据,即由于多种可能的原因,不适合直接在应用程序中使用。因此,我们需要首先对其进行分析,执行必要的预处理,然后再使用它。

例如,假设我们正在尝试构建cat分类器。我们的程序将图像作为输入,然后告诉我们图像是否包含猫。建立该分类器的第一步是收集数百张猫图片。一个普遍的问题是,我们抓取的所有图片都不会具有相同的尺寸/尺寸,因此在将它们输入模型进行训练之前,我们需要将所有尺寸调整/预处理为标准尺寸。

这只是图像处理对于任何计算机视觉应用必不可少的众多原因之一。

先决条件

在继续进行之前,让我们讨论一下您需要了解的内容,以便轻松地学习本教程。首先,您应该掌握任何语言的基本编程知识。其次,您应该知道什么是机器学习以及它如何工作的基础,因为本文中我们将使用一些机器学习算法进行图像处理。另外,如果您在继续学习本教程之前对Open CV有任何了解或基础知识,这将对您有所帮助。但这不是必需的。

为了遵循本教程,您绝对应该知道的一件事是图像在内存中的准确表示方式。每个图像由一组像素表示,即像素值矩阵。对于灰度图像,像素值的范围是0到255,它们代表该像素的强度。例如,如果您具有20 x 20尺寸的图像,则将以20×20的矩阵(总共400个像素值)表示。

如果要处理彩色图像,则应该知道它将具有三个通道-红色,绿色和蓝色(RGB)。因此,对于单个图像将存在三个这样的矩阵。

安装

注意:由于我们将通过Python使用OpenCV,因此隐含的要求是您的工作站上已经安装了Python(版本3)。

Windows

$ pip install opencv-python

MacOS

$ brew install opencv3 --with-contrib --with-python3

Linux

$ sudo apt-get install libopencv-dev python-opencv

要检查安装是否成功,请在Python Shell或命令提示符中运行以下命令:

import cv2

您应该知道的一些基本知识

在我们继续在应用程序中使用图像处理之前,重要的是要了解哪种操作属于此类,以及如何进行这些操作。这些操作以及其他操作将在以后的应用程序中使用。所以,让我们开始吧。

对于本文,我们将使用以下图像:

注意:为了在本文中显示图像,已对图像进行了缩放,但是我们使用的原始大小约为1180×786。

您可能已经注意到图像当前是彩色的,这意味着它由三个颜色通道表示,即红色,绿色和蓝色。我们将图像转换为灰度图像,并使用下面的代码将图像分为单独的通道。

查找图像细节

在使用imread()函数加载图像后,我们可以检索有关图像的一些简单属性,例如像素数和尺寸: 

import cv2

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

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

输出: 

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,而所有值小于或等于125的像素将被分配值为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))

下面显示了在盐和胡椒粉噪声下对图像应用Contraharmonic Mean Filter 所得到的图像。与原始灰度图像进行比较后,我们可以看到它已复制了几乎与原始图像完全相同的图像。其强度/亮度级别相同,并且也突出了玫瑰上的亮点。因此,我们可以得出结论,逆谐波均值滤波器在处理盐和胡椒噪声方面非常有效。

谐波平均滤波器输出:

既然我们已经找到了从嘈杂的图像中恢复原始图像的最佳过滤器,那么我们可以继续下一个应用程序了。

#2:使用Canny Edge Detector进行边缘检测

到目前为止,我们一直在使用的玫瑰图像具有恒定的背景,即黑色,因此,对于该应用程序,我们将使用不同的图像以更好地显示算法的功能。原因是,如果背景恒定,则边缘检测任务将变得非常简单,我们不希望这样做。

在本教程的前面,我们讨论了cat分类器,让我们继续前面的示例,看看图像处理如何在其中发挥不可或缺的作用。

在分类算法中,首先会扫描图像中的“对象”,即,当您输入图像时,算法会在该图像中找到所有对象,然后将它们与您要查找的对象的特征进行比较。如果是猫分类器,它将对图像中找到的所有对象与猫图像的特征进行比较,如果找到匹配项,它将告诉我们输入图像包含猫。

由于我们以cat分类器为例,因此公平地使用cat图像是公平的。下面是我们将要使用的图像:

用于边缘检测的图像:

 

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 Edge Detector,它是如何实现的?现在让我们讨论一下。

要理解上述内容,需要讨论三个关键步骤。首先,它以与我们前面讨论的相似的方式对图像执行降噪。其次,它在每个像素处使用一阶导数来查找边缘。其背后的逻辑是,存在边缘的点存在突然的强度变化,这会导致一阶导数的值出现尖峰,因此使该像素成为“边缘像素”。

最后,它执行磁滞阈值;上面我们说过,边上的一阶导数的值有一个峰值,但是我们没有说明将峰值归为边缘需要“多高”-这称为阈值!在本教程的前面,我们讨论了简单的阈值化。磁滞阈值是对此的改进,它使用两个阈值而不是一个。其背后的原因是,如果阈值太高,我们可能会错过一些实际边缘(真负值),而如果阈值太低,我们会得到很多被归类为实际上不是边缘的边缘(假正值)的点。 )。将一个阈值设置为高,将一个阈值设置为低。所有高于“高阈值”的点都被标识为边缘,然后评估所有高于低阈值但低于高阈值的点;被标识为边的点附近或与其相邻的点也被标识为边,其余的点被丢弃。

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

结论

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

我们继续讨论了什么是图像处理及其在机器学习的计算机视觉领域中的用途。我们讨论了一些常见的噪声类型,以及如何在应用程序中使用图像之前使用不同的滤镜将其从图像中去除。

此外,我们了解了图像处理如何在“对象检测”或“分类”等高端应用程序中发挥不可或缺的作用。请注意,本文只是冰山一角,而且Digital Image Processing商店中还有很多东西,不可能在单个教程中介绍。阅读此书可以使您更深入地学习和了解与图像处理有关的其他高级概念。祝好运!