OpenCV轻松入门:面向Python【1.1】

1.2.3 保存图像

OpenCV 提供了函数 cv2.imwrite(),用来保存图像,该函数的语法格式为:

retval = cv2.imwrite( filename, img[, params] )

式中:

 retval 是返回值。如果保存成功,则返回逻辑值真(True);如果保存不成功,则返回逻

辑值假(False)。

 filename 是要保存的目标文件的完整路径名,包含文件扩展名。

 img 是被保存图像的名称。

 params 是保存类型参数,是可选的。

【例 1.7】 编写一个程序,将读取的图像保存到当前目录下。根据题目要求,编写代码如下:

import cv2 lena=cv2.imread("lena.bmp") r=cv2.imwrite("result.bmp",lena)

上述程序会先读取当前目录下的图像 lena.bmp,生成它的一个副本图像,然后将该图像以名称 result.bmp 存储到当前目录下。

1.3 OpenCV 贡献库

目前, OpenCV 库包含如下两部分。

 OpenCV 主库:即通常安装的 OpenCV 库,该库是成熟稳定的,由核心的 OpenCV 团队维护。

 OpenCV 贡献库:该扩展库的名称为 opencv_contrib,主要由社区开发和维护,其包含的视觉应用比 OpenCV 主库更全面。需要注意的是, OpenCV 贡献库中包含非 OpenCV许可的部分,并且包含受专利保护的算法。因此,在使用该模块前需要特别注意。OpenCV 贡献库中包含了非常多的扩展模块,举例如下。

 bioinspired:生物视觉模块。

 datasets:数据集读取模块。

 dnn:深度神经网络模块。

 face:人脸识别模块。

 matlab: MATLAB 接口模块。

 stereo:双目立体匹配模块。

 text:视觉文本匹配模块。

 tracking:基于视觉的目标跟踪模块。

 ximgpro:图像处理扩展模块。

 xobjdetect:增强 2D 目标检测模块。

 xphoto:计算摄影扩展模块。

可以通过以下两种方式使用贡献库:

 下载 OpenCV 贡献库,使用 cmake 手动编译。

 通过语句 pip install opencv-contrib-python 直接安装编译好的 OpenCV 贡献库。网页

https://pypi.org/project/opencv-contrib-python/上提供了该方案的常见问题列表 FAQ

(Frequently Asked Questions),而且该 FAQ 是不断更新的。

第 2 章 图像处理基础

本章主要介绍图像的基本表示方法、像素的访问和操作、感兴趣区域处理、通道处理等知识点。需要强调的是,使用面向 Python 的 OpenCV(OpenCV for Python)必须熟练掌握 Numpy库,尤其是 Numpy.array 库, Numpy.array 库是 Python 处理图像的基础。

2.1 图像的基本表示方法

本节主要讨论二值图像、灰度图像、彩色图像的基本表示方法。1.二值图像

二值图像是指仅仅包含黑色和白色两种颜色的图像。

在计算机中,通过一个栅格状排列的数据集(矩阵)来表示和处理图像。例如, 图 2-1 是一个字母 A 的图像,计算机在处理该图像时,会首先将其划分为一个个的小方块,每一个小方块就是一个独立的处理单位,称为像素点。接下来,计算机会将其中的白色像素点(白色小方块区域)处理为“1”, 将黑色像素点(黑色小方块区域)处理为“0”,以方便进行后续的存储和处理等操作。

按照上述处理方式, 图 2-1 中的字母 A 在计算机内的存储形式如图 2-2 所示。

上述图像比较简单,图像内只有黑色和白色两种不同的颜色,因此只使用一个比特位(0或者 1)就能表示。

2.灰度图像

二值图像表示起来简单方便,但是因为其仅有黑白两种颜色,所表示的图像不够细腻。如果想要表现更多的细节,就需要使用更多的颜色。例如, 图 2-3 中的 lena 图像是一幅灰度图像,它采用了更多的数值以体现不同的颜色,因此该图像的细节信息更丰富。

通常,计算机会将灰度处理为 256 个灰度级,用数值区间[0, 255]来表示。其中,数值“255”表示纯白色,数值“0”表示纯黑色,其余的数值表示从纯白到纯黑之间不同级别的灰度。

用于表示 256 个灰度级的数值 0~255,正好可以用一个字节( 8 位二进制值)来表示。表 2-1 所示的是部分二进制值所对应的十进制值及灰度颜色。

按照上述方法, 图 2-3 中的图像需要使用一个各行各列的数值都在[0, 255]之间的矩阵来表示。例如, 图 2-4 就是图 2-3 的 lena 图像中部分区域的数值表示形式。

有些情况下,也会使用 8 位二进制值来表示一幅二值图像。这种情况下,使用灰度值 255表示白色、灰度值 0 表示黑色。此时,该二值图像内仅有数值 0 和数值 255 两种类型的灰度值(灰度级),不存在其他灰度值的像素点。

3.彩色图像

相比二值图像和灰度图像,彩色图像是更常见的一类图像,它能表现更丰富的细节信息。

神经生理学实验发现,在视网膜上存在三种不同的颜色感受器,能够感受三种不同的颜色:红色、绿色和蓝色,即三基色。 自然界中常见的各种色光都可以通过将三基色按照一定的比例混合构成。除此以外,从光学角度出发,可以将颜色解析为主波长、纯度、明度等。从心理学和视觉角度出发,可以将颜色解析为色调、饱和度、亮度等。通常, 我们将上述采用不同的方式表述颜色的模式称为色彩空间,或者颜色空间、颜色模式等。

虽然不同的色彩空间具有不同的表示方式,但是各种色彩空间之间可以根据需要按照公式进行转换。这里仅仅介绍较为常用的 RGB 色彩空间。

在 RGB 色彩空间中,存在 R(red,红色)通道、 G(green,绿色)通道和 B(blue,蓝色)通道,共三个通道。每个色彩通道值的范围都在[0, 255]之间,我们用这三个色彩通道的组合表示颜色。

以比较通俗的方式来解释就是,有三个油漆桶,分别装了红色、绿色、蓝色的油漆,我们分别从每个油漆桶中取容量为 0~255 个单位的不等量的油漆,将三种油漆混合就可以调配出一种新的颜色。三种油漆经过不同的组合,共可以调配出所有常见的 256×256×256=16 777 216种颜色。

表 2-2 展示了不同的 RGB 值所对应的颜色。

例如,对于图 2-5 左侧的彩色图像,可以理解为由右侧的 R 通道、 G 通道、 B 通道三个通道构成。其中,每一个通道都可以理解为一个独立的灰度图像。左侧彩色图像中的白色方块内的区域对应右侧三个通道的三个矩阵,白色方块左上角顶点的 RGB 值为(205,89,68)。

因此,通常用一个三维数组来表示一幅 RGB 色彩空间的彩色图像。

一般情况下,在 RGB 色彩空间中,图像通道的顺序是 R→G→B,即第 1 个通道是 R 通道,第 2 个通道是 G 通道,第 3 个通道是 B 通道。需要特别注意的是,在 OpenCV 中,通道的顺序是 B→G→R,即:

 第 1 个通道保存 B 通道的信息。

 第 2 个通道保存 G 通道的信息。

 第 3 个通道保存 R 通道的信息。

在图像处理过程中,可以根据需要对图像的通道顺序进行转换。除此以外,还可以根据需要对不同色彩空间的图像进行类型转换,例如,将灰度图像处理为二值图像,将彩色图像处理为灰度图像等。

2.2 像素处理

像素是图像构成的基本单位,像素处理是图像处理的基本操作,可以通过位置索引的形式对图像内的元素进行访问、处理。

1.二值图像及灰度图像

需要说明的是,在 OpenCV 中,最小的数据类型是无符号的 8 位数。因此,在 OpenCV 中实际上并没有二值图像这种数据类型,二值图像经常是通过处理得到的,然后使用0表示黑色,使用 255 表示白色。

可以将二值图像理解为特殊的灰度图像,这里仅以灰度图像为例讨论像素的读取和修改。通过 2.1 节的分析可知,可以将图像理解为一个矩阵,在面向 Python 的 OpenCV(OpenCV for Python) 中,图像就是 Numpy 库中的数组。一个 OpenCV 灰度图像是一个二维数组,可以使用表达式访问其中的像素值。例如,可以使用 image[0,0]访问图像 image 第 0 行第 0 列位置上的像素点。第 0 行第 0 列位于图像的左上角,其中第 1 个索引表示第 0 行,第 2 个索引表示第0 列。

为了方便理解,我们首先使用 Numpy 库来生成一个 8×8 大小的数组,用来模拟一个黑色图像,并对其进行简单处理。

【例 2.1】 使用 Numpy 库生成一个元素值都是 0 的二维数组,用来模拟一幅黑色图像,并对其进行访问、修改。

分析:使用 Numpy 库中的函数 zeros()可以生成一个元素值都是 0 的数组,并可以直接使用数组的索引对其进行访问、修改。

根据题目要求及分析,编写代码如下:

import cv2
import numpy as np
img=np.zeros((8,8),dtype=np.uint8)
print("img=\n",img)
cv2.imshow("one",img)
print("读取像素点 img[0,3]=",img[0,3])
img[0,3]=255
print("修改后 img=\n",img)
print("读取修改后像素点 img[0,3]=",img[0,3])
cv2.imshow("two",img)
cv2.waitKey()
cv2.destroyAllWindows()

代码分析如下。

 使用函数 zeros()生成了一个 8×8 大小的二维数组,其中所有的值都是 0,数值类型是

np.uint8。根据该数组的属性,可以将其看成一个黑色的图像。

 语句 img[0,3]访问的是 img 第 0 行第 3 列的像素点,需要注意的是,行序号、列序号都是从 0 开始的。

 语句 img[0,3]=255 将 img 中第 0 行第 3 列的像素点的像素值设置为“255”。

运行上述程序,会出现名为 one 和 two 的两个非常小的窗口,其中:

 名为 one 的窗口是一个纯黑色的图像。

 名为 two 的窗口在顶部靠近中间位置有一个白点(对应修改后的值 255),其他地方也都是纯黑的图像。

同时,在控制台会输出如下内容:

通过本例中两个窗口显示的图像可知,二维数组与图像之间存在对应关系。因为窗口太小,在纸质图书上可能无法直接观察效果,请大家在计算机上运行上述程序,观察效果。

【例 2.2】 读取一个灰度图像,并对其像素进行访问、修改。根据题目要求,编写代码如下:

import cv2
img=cv2.imread("lena.bmp",0)
cv2.imshow("before",img)
for i in range(10,100):
for j in range(80,100):
img[i,j]=255
cv2.imshow("after",img)
cv2.waitKey()
cv2.destroyAllWindows()

在本例中,使用了一个嵌套循环语句,将图像 img 中“第 10 行到 99 行”与“第 80 列到99 列”交叉区域内的像素值设置为 255。从图像 img 上来看,该交叉区域被设置为白色。

运行程序,结果如图 2-6 所示,其中:

 左图是读取的原始图像。

 右图是经过修改后的图像。

2.彩色图像

RGB 模式的彩色图像在读入 OpenCV 内进行处理时,会按照行方向依次读取该 RGB 图像的 B 通道、 G 通道、 R 通道的像素点,并将像素点以行为单位存储在 ndarray 的列中。例如,有一幅大小为 R 行×C 列的原始 RGB 图像,其在 OpenCV 内以 BGR 模式的三维数组形式存储,如图 2-7 所示。

可以使用表达式访问数组内的值。例如,可以使用 image[0,0,0]访问图像 image 的 B 通道内的第 0 行第 0 列上的像素点, 式中:
 第 1 个索引表示第 0 行。

 第 2 个索引表示第 0 列。

 第 3 个索引表示第 0 个颜色通道。

根据上述分析可知,假设有一个红色(其 R 通道值为 255, G 通道值为 0, B 通道值为 0)图像, 不同的访问方式得到的值如下。

 img[0,0]:访问图像 img 第 0 行第 0 列像素点的 BGR 值。图像是 BGR 格式的,得到的数值为[0,0,255]。

 img[0,0,0]:访问图像 img 第 0 行第 0 列第 0 个通道的像素值。图像是 BGR 格式的,所以第 0 个通道是 B 通道,会得到 B 通道内第 0 行第 0 列的位置所对应的值 0。

 img[0,0,1]:访问图像 img 第 0 行第 0 列第 1 个通道的像素值。图像是 BGR 格式的,所以第 1 个通道是 G 通道,会得到 G 通道内第 0 行第 0 列的位置所对应的值 0。

 img[0,0,2]:访问图像 img 第 0 行第 0 列第 2 个通道的像素值。图像是 BGR 格式的,所以第 2 个通道是 R 通道,会得到 R 通道内第 0 行第 0 列的位置所对应的值 255。

为了方便理解,我们首先使用 Numpy 库来生成一个 2×4×3 大小的数组,用它模拟一幅黑色图像,并对其进行简单处理。

【例 2.3】 使用 Numpy 生成三维数组,用来观察三个通道值的变化情况。根据题目要求,编写代码如下:

import numpy as np
import cv2
#-----------蓝色通道值--------------
blue=np.zeros((300,300,3),dtype=np.uint8)
blue[:,:,0]=255
print("blue=\n",blue)
cv2.imshow("blue",blue)
#-----------绿色通道值--------------
green=np.zeros((300,300,3),dtype=np.uint8)
green[:,:,1]=255
print("green=\n",green)
cv2.imshow("green",green)
#-----------红色通道值--------------
red=np.zeros((300,300,3),dtype=np.uint8)
red[:,:,2]=255
print("red=\n",red)
cv2.imshow("red",red)
#-----------释放窗口--------------
cv2.waitKey()
cv2.destroyAllWindows()

在本例中,分别生成了 blue、 green、 red 三个数组,其初始值都是 0。接下来,分别改变各通道值。

 针对数组 blue,将其第 0 个通道的值设置为 255。从图像角度来看,图像 blue 的 B 通道值为 255,其余两个通道值为 0,因此图像 blue 为蓝色图像。

 针对数组 green,将其第 1 个通道的值设置为 255。从图像角度来看,图像 green 的 G通道值为 255,其余两个通道值为 0,因此图像 green 为绿色图像。

 针对数组 red,将其第 2 个通道的值设置为 255。从图像角度来看,图像 red 的 R 通道值为 255,其余两个通道值为 0,因此图像 red 为红色图像。

运行上述程序,会显示颜色为蓝色、绿色、红色的三幅图像,分别对应数组 blue、数组 green、数组 red。因为黑白印刷无法显示彩色图像,所以请读者运行程序后观察结果。

除了显示图像,还会显示每个数组的输出值,部分输出结果如图 2-8 所示。

【例 2.4】 使用 Numpy 生成一个三维数组,用来观察三个通道值的变化情况。根据题目要求,编写代码如下:
 

import numpy as np
import cv2
img=np.zeros((300,300,3),dtype=np.uint8)
img[:,0:100,0]=255
img[:,100:200,1]=255
img[:,200:300,2]=255
print("img=\n",img)
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()

运行上述程序,会显示如图 2-9 所示的图像。 由于本书为黑白印刷,所以为了更好地观察运行效果,请大家亲自上机运行程序。

除了显示图像,还会显示 img 值的情况,部分输出结果如图 2-10 所示。

【例 2.5】 使用 Numpy 生成一个三维数组,用来模拟一幅 BGR 模式的彩色图像,并对其进行访问、修改。

分析:使用 Numpy 中的 zeros()函数可以生成一个元素值都是 0 的数组。可以直接使用数组的索引形式对其进行访问、修改。

根据题目要求及分析,编写代码如下:

1. import numpy as np
2. img=np.zeros((2,4,3),dtype=np.uint8)
3. print("img=\n",img)
4. print("读取像素点 img[0,3]=",img[0,3])
5. print("读取像素点 img[1,2,2]=",img[1,2,2])
6. img[0,3]=255
7. img[0,0]=[66,77,88]
8. img[1,1,1]=3
9. img[1,2,2]=4
10. img[0,2,0]=5
11. print("修改后 img\n",img)
12. print("读取修改后像素点 img[1,2,2]=",img[1,2,2])

本程序进行了如下操作。

 第 2 行使用 zeros()生成一个 2×4×3 大小的数组,其对应一个“2 行 4 列 3 个通道”的BGR 图像。

 第 3 行使用 print 语句显示(打印)当前图像(数组)的值。

 第 4 行中的 img[0,3]语句会访问第 0 行第 3 列位置上的 B 通道、 G 通道、 R 通道三个像素点。

 第 5 行的 img[1,2,2]语句会访问第 1 行第 2 列第 2 个通道位置上的像素点。

 第 6 行的 img[0,3]=255 语句会修改 img 中第 0 行第 3 列位置上的像素值,该位置上的 B通道、 G 通道、 R 通道三个像素点的值都会被修改为 255。

 第 7 行的 img[0,0]=[66,77,88]语句会修改 img 中第 0 行第 0 列位置上的 B 通道、G 通道、

R 通道上三个像素点的值,将它们修改为[66,77,88]。

 第 8 行的 img[1,1,1]=3 语句会修改 img 中第 1 行第 1 列第 1 个通道(G 通道)位置上的像素值,将其修改为 3。

 第 9 行的 img[1,2,2]=4 语句会修改 img 中第 1 行第 2 列第 2 个通道(R 通道)位置上的

像素值,将其修改为 4。

 第 10 行的 img[0,2,0]=5 语句会修改 img 中第 0 行第 2 列第 0 个通道(B 通道) 位置上

的像素值,将其修改为 5。

 最后两行使用 print 语句观察 img 和 img[1,2,2]的值。

运行上述程序,会在控制台输出如下结果:

在本例中,为了方便说明问题,设置的数组比较小。在实际中可以定义稍大的数组,并使用 cv2.imshow()将其显示出来,进一步观察处理结果,加深理解。

【例 2.6】 读取一幅彩色图像,并对其像素进行访问、修改。根据题目要求,编写代码如下:

1. import cv2
2. img=cv2.imread("lenacolor.png")
3. cv2.imshow("before",img)
4. print("访问 img[0,0]=",img[0,0])
5. print("访问 img[0,0,0]=",img[0,0,0])
6. print("访问 img[0,0,1]=",img[0,0,1])
7. print("访问 img[0,0,2]=",img[0,0,2])
8. print("访问 img[50,0]=",img[50,0])
9. print("访问 img[100,0]=",img[100,0])
10. #区域 1
11. for i in range(0,50):
12. for j in range(0,100):
13. for k in range(0,3):
14. img[i,j,k]=255 #白色
15. #区域 2
16. for i in range(50,100):
17. for j in range(0,100):
18. img[i,j]=[128,128,128] #灰色
19. #区域 3
20. for i in range(100,150):
21. for j in range(0,100):
22. img[i,j]=0 #黑色
23. cv2.imshow("after",img)
24. print("修改后 img[0,0]=",img[0,0])
25. print("修改后 img[0,0,0]=",img[0,0,0])
26. print("修改后 img[0,0,1]=",img[0,0,1])
27. print("修改后 img[0,0,2]=",img[0,0,2])
28. print("修改后 img[50,0]=",img[50,0])
29. print("修改后 img[100,0]=",img[100,0])
30. cv2.waitKey()
31. cv2.destroyAllWindows()

上述程序进行了如下操作。

 第 2 行使用 imread()函数读取当前目录下的一幅彩色 RGB 图像。

 第 4 行的 img[0,0]语句会访问 img 中第 0 行第 0 列位置上的 B 通道、 G 通道、 R 通道三个像素点。

 第 5~7 行分别会访问 img 中第 0 行第 0 列位置上的 B 通道、 G 通道、 R 通道三个像素点。

 第 8 行的 img[50,0]语句会访问第 50 行第 0 列位置上的 B 通道、 G 通道、 R 通道三个像素点。

 第 9 行的 img[100,0]语句会访问第 100 行第 0 列位置上的 B 通道、 G 通道、 R 通道三个像素点。

 第 10~14 行使用三个 for 语句的嵌套循环,对图像左上角区域(即“第 0 行到第 49 行”与“第 0 列到第 99 列”的行列交叉区域,我们称之为区域 1)内的像素值进行设定。

借助 img[i, j, k]=255 语句将该区域内的三个通道的像素值都设置为 255,让该区域变为白色。

 第 15~18 行使用两个 for 语句的嵌套循环,对图像左上角位于区域 1 正下方的区域(即

“第 50 行到第 99 行”与“第 0 列到第 99 列”的行列交叉区域,我们称之为区域 2)

内的像素值进行设定。 借助 img[i, j]=[128,128,128]语句将该区域内的三个通道的像素值都设置为 128,让该区域变为灰色。

 第 19~21 行使用两个 for 语句的嵌套循环,对图像左上角位于区域 2 正下方的区域(即

“第 100 行到第 149 行”与“第 0 列到第 99 列”的行列交叉区域,我们称之为区域 3)内的像素值进行设定。 借助 img[i, j]=0 语句将该区域内的三个通道的像素值都设置为 0,让该区域变为黑色。

运行程序,结果如图 2-11 所示, 其中左图是读取的原始图像,右图是经过修改后的图像。

同时,在控制台会输出如下内容:
 

访问 img[0,0]= [125 137 226]
访问 img[0,0,0]= 125
访问 img[0,0,1]= 137
访问 img[0,0,2]= 226
访问 img[50,0]= [114 136 230]
访问 img[100,0]= [ 75 55 155]
修改后 img[0,0]= [255 255 255]
修改后 img[0,0,0]= 255
修改后 img[0,0,1]= 255
修改后 img[0,0,2]= 255
修改后 img[50,0]= [128 128 128]
修改后 img[100,0]= [0 0 0]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BinaryStarXin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值