随着最近在IT行业的崛起,计算机视觉(CV)已经成为人工智能(AI)中最有前途的领域之一。图像处理,因此是CV的基础。在这一系列的文章中,我们将介绍它的基础知识。当你跟随这个在线教程时,你将在Python编程语言中的OpenCV库的帮助下,学习图像处理的一些核心概念。
在这篇文章中,我们包括
1.安装和设置
在这篇文章中,你将需要以下库:NumPy和OpenCV。Pip是在Python中安装外部库的最简单方法。用以下步骤进行安装。
1.1 OpenCV的安装
要在Windows中安装OpenCV和NumPy,请使用以下命令。
pip install numpy
pip install opencv-python
要在Linux中做同样的事情,在终端使用这些命令。
$pip3 install opencv-python
$pip3 install numpy

1.2 安装
为了导入OpenCV和Numpy,在你的Python文件中添加这些行。
import numpy as np
import cv2
2.图像表示法(图像的数学表示法)
21世纪是一个数字化的世纪。数字信号,如电信信号、音频信号、电信号正在占据主导地位。图像也不例外。将图像转换为数字形式使得图像处理变得更加容易。让我们深入了解图像在计算机内存中是如何以数字形式表示的。
2.1 作为矩阵的图像
图像被表示为二维矩阵。矩阵的每个元素代表一个像素,每个像素的值决定了一个颜色的强度。像素的位置是由它在相应的行和列中的位置决定的。
图像的表示方法有很多,其中最流行的两种是:灰度和RGB。灰度图片就是我们所知道的黑白图片,而RGB是一种多色图片。我们将在后面深入介绍这些概念。

3.用OpenCV加载图像
在本节中,我们将加载、显示和保存图像。本文中使用的所有图片都可以在这里找到。
3.1 加载一个图像
使用OpenCV的imread函数加载图像是一项非常简单的任务。Imread需要两个参数。第一个是我们想要加载的图片的路径,第二个是指定我们想要的图片的类型(灰度,彩色或不变)。
当我们想加载彩色图片时,第二个参数是1。
img_color = cv2.imread('/content/lena_color.png', 1) #1 == cv2.IMREAD_COLOR you can pass both parameters
当我们想加载灰度图片时,第二个参数为0。
img_gray = cv2.imread('/content/lena_color.png', 0) #0 == cv2.IMREAD_GRAYSCALE you can pass both parameters
3.2 显示图像
显示一张图片可以在 OpenCV的imshow函数的帮助下完成 。Imshow也需要两个参数。一个字符串,代表要显示图像的窗口名称和存储图像的变量名称。
显示彩色图片
#Showing color image
cv2.imshow('Colored picture', img_color)
显示灰度图片
#Showing gray image
cv2.imshow('Gray picture', img_gray)

3.3 保存图像
在进行了某些操作后,我们想保存已改变的图像。幸运的是,也有一个函数用于此。该函数的名称是imwrite,它也需要两个参数。第一个是新图像文件的名称,第二个是存储我们想保存的图像的变量名称。
#Saving a colored image
cv2.imwrite('img_color.png', img_color)
#Saving a gray image
cv2.imwrite('img_gray.png', img_gray)
4.图像是一个数组
正如我们已经提到的,图像被表示为矩阵,它是二维数据结构,数字被排列成行和列。
比如说:

这个矩阵有3行和4列。
在Python中,我们没有内置的矩阵,但是我们把它们写成一个数组。数组的数量代表行的数量,而数组元素的数量(数组的长度),代表列的数量。
对于这个具体的例子,我们会写成。
#Defining a list of lists
A = [[2,-5,-11,0],
[-9,4,6,13],
[4,7,12,-2]]
我们也可以使用numpy库来创建新的矩阵。
import numpy as np
A = np.array([[2,-5,-11,0],
[-9,4,6,13],
[4,7,12,-2]])
OpenCV中的图片是以numpy数组的形式存储的,不同的是用于存储图片的矩阵要比这个大得多。
到目前为止,我们已经看到我们可以同时显示灰度和彩色图片。那么当涉及到存储时,它们之间有什么区别呢?

4.1 灰度图像
灰度图像由一个通道组成,或者更准确地说,是一个矩阵,其中每个像素值都代表该像素的强度。像素值范围为0-255,用uint8或8位表示(每个像素用8位表示)。靠近0的是较暗的色调,其中0是黑色,自然靠近255的值是较亮的,值255是白色。
0和255之间的值代表灰色的色调。我们之前加载的图像,当存储在一个矩阵中时,看起来会是这样的。
img_gray = cv2.imread('/content/lena_color.png', 0)
#attribute shape returns number of rows and columns in matrix
print(img_gray.shape) # => (512, 512)
我们可以看到,之前显示的莉娜的灰色图片,实际上是一个512×512的矩阵。
4.2 RGB图像
RGB是指红-绿-蓝。我们称它们为三原色,原因是我们只能看到这三种颜色。我们看到的其他每一种颜色都只是这三种颜色的组合。彩色图像利用人类眼睛的这一特性来模仿所有的颜色。
基本上,RGB图像是三个串联的矩阵,每个矩阵的像素值都代表给定颜色的阴影(红-绿-蓝)。将这三个矩阵结合起来,我们就得到了一个彩色的图片。
加载一个彩色图片,会给我们一个看起来像这样的矩阵。
img_color = cv2.imread('/content/lena_color.png', 1)
print(img_color.shape) # => (512, 512, 3)
我们可以看到,图像被表示为三个矩阵串联在一个数组中。与灰度图像不同,现在有三个通道,每个通道代表一种颜色。 值得一提的是,在OpenCV中,通道的顺序是相反的。这意味着首先是蓝色, 其次是红色, 第三是绿色(BGR)。

5.图像索引
有时我们想改变图像的某一部分,或者只是一个像素。因此,我们需要以某种方式访问这些部分并改变其值。正如我们之前提到的,每个像素的位置是由图像矩阵中的行和列定义的。考虑到这一点,我们将展示图像索引到底是如何完成的。
5.1 访问像素值
图像索引与python中的列表索引非常相似。唯一的区别是,在图像中我们有两个坐标。让我们从一个图像中选择一个像素。
import numpy as np
import cv2
#Indexing image to get value of a single pixel, first index represents row and second index represents column
img_gray = cv2.imread('/content/lena_color.png', 0)
pixel_value = img_color[156, 264]
print(pixel_value) # => 175

正如你所看到的,第157行和第265列的像素的值是175。需要注意的是,行在前,列在后,这一点非常重要。如果我们想选择多个像素值,我们也可以这样做,就像我们用列表做的那样,但同样我们有两个坐标。
例如,让我们选择我们图像中的第四行。正如我们之前看到的,我们的图像是一个尺寸为516×516的数组,所以对于一行我们将得到一个尺寸为1×516的数组。
import numpy as np
import cv2
#Indexing image to get value of the fourth row
img_gray = cv2.imread('/content/lena_color.png', 0)
fourth_row = img_gray[4, :]
print(fourth_row.shape) # => (516,)
我们也可以使用行和列的间隔索引来获得图像的一部分。我们将从156到159的所有行,以及4到7的所有列。让我们看看我们得到了什么。
import numpy as np
import cv2
#Indexing image to get value of part of an image
img_gray = cv2.imread('/content/lena_color.png', 0)
snipped_img = img_gray[156:159, 4:7]
print(fourth_row)# => [[111 110 110]
# [110 109 109]
# [106 110 112]]

我们用这种索引方式剪下了图片的一部分,这非常有用。另外,在Python中我们有一种叫做逻辑索引的东西。有时我们需要满足某些条件的所有像素。让我们选择所有等于255的像素。
import numpy as np
import cv2
img_gray = cv2.imread('/content/lena_color.png', 0)
print([img_gray == 255]) # => [[False, False, False, ..., False, False, False]], logical matrix True/False
print(img_gray[img_gray == 255]) # => [], None of pixels have value 255
我们可以看到我们的逻辑矩阵充满了假值,这意味着没有一个像素满足我们的条件。
5.2 改变像素值
现在我们知道了如何访问像素值,我们可以轻松地改变它们。例如,我们可以为图像中的每个位置分配一个新的、恒定的像素值,或者我们可以对选定的像素进行一些数学运算,将它们转化为我们需要的方式。这就是这两种方法的工作原理。
import numpy as np
import cv2
#Indexing image to change its values
img_gray = cv2.imread('/content/lena_color.png', 0)
#Assigning new value to an image position
img_gray[75, 4] = 231
print(img_gray[75, 4]) # => 231
#Doing mathematical operations on part of an image
img_gray[156:159, 4:7] = img_gray[156:159, 4:7] - 6
print(img_gray[156:159, 4:7]) # => [[105 104 104]
# [104 103 103]
# [100 104 106]]
5.3 OpenCV实例
现在我们已经学会了如何改变像素值,让我们看看当我们显示一幅图像时它的实际效果。首先,我们要加载我们的图片,看看如果我们将所有的像素值增加某个数字会发生什么。
img_gray = cv2.imread('/content/baboon.png', 0)
cv2.imshow(img_gray)
new_img = img_gray+100
cv2.imshow(new_img)

通过将所有像素值增加100,图片整体上应该更亮。那么,为什么某些区域会比以前更暗呢?
这是因为在操作之后,如果像素值超过了255,它就不会再去超过它,而是循环往复,从刻度的底部开始。
因此,如果一个像素之前的值是240,而我们添加了100,那么它现在的值将是85.通常,我们不希望发生这种情况,而是将其限定在255.我们可以通过使用OpenCV的添加函数来做到这一点。第一个参数指定图像,第二个参数决定我们要添加的数量。
Img_gray_cap= cv2.add(img_gray,100)
cv2.imshow(img_gray_cap)

正如我们所看到的,没有更深的颜色,这意味着会超过限制的值被封住了。现在让我们来执行增加图像内的对比度这一简单任务。基本上,我们想让明亮的像素更加明亮,而黑暗的像素更加黑暗。
img_gray[img_gray>150] = 255
img_gray[img_gray<100] = 0
cv2_imshow(img_gray)
大于150的像素被赋予255的值,而低于100的像素被赋予0的值,结果如下。

结论
根据不同的情况,你要使用不同的可视化、索引和赋值方法。图像处理需要大量的经验和知识。通过这篇文章,你刚刚迈出了学习图像处理和计算机视觉的第一步。我们鼓励你继续关注我们的系列报道。我们将涵盖OpenCV和图像处理的更多基本概念。在你征服了这些基础知识之后,我们将做一些项目和更多的高级概念。