书名:计算机视觉实践教程:基于PyTorch
ISBN:978-7-115-66318-4
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 谢 源 高 岩 全红艳
责任编辑 佘 洁
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
网址 http://www.ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
本书内容分为两部分:计算机视觉实践和实践参考代码及解析。其中,计算机视觉实践部分涵盖计算机视觉实践基础、软件环境安装及图像显示、滤波处理与多层特征表达、图像特征提取与可视化、图像特征对应与对齐、二值图像的特征计算、图像分割、语义分割、纹理特征计算、摄像机模型参数恢复、光流计算、三维重建技术、传统目标识别技术、传统目标跟踪技术、智能目标识别与跟踪、点云语义分割、点云三维重建和视觉语言模型;实践参考代码及解析部分则提供了计算机视觉实践部分的实践参考代码及解析。
本书可作为计算机科学与技术、软件工程及电子类专业的本科生、研究生和教师的实践参考书,也可作为计算机应用工程领域开发人员的参考资料。对于未接触过计算机视觉先行课程的读者,本书提供了易于理解和循序渐进的学习路径,帮助他们掌握计算机视觉的算法实践。
本书是一部融合了计算机视觉智能技术与计算机视觉传统技术的实践教材。与传统实践教材相比,本书的特色在于介绍了计算机视觉理论基础知识,并结合智能前沿技术将深度学习实践内容整合到实践教学中。本书可以与理论课程教材《计算机视觉导论:从传统方法到智能技术》配套使用,也可以独立应用于计算机视觉课程的实践教学。
本书内容分为两大部分:计算机视觉实践和实践参考代码及解析。覆盖的实践主题包括计算机视觉实践基础、软件环境安装及图像显示、滤波处理与多层特征表达、图像特征提取与可视化、图像特征对应与对齐、二值图像的特征计算、图像分割、语义分割、纹理特征计算、摄像机模型参数恢复、光流计算、三维重建技术、传统目标识别技术、传统目标跟踪技术、智能目标识别与跟踪、点云语义分割、点云三维重建和视觉语言模型。
本书在内容编排上遵循由浅入深、循序渐进的原则。每个实践单元不仅涵盖基础知识夯实、实践内容设置、实践过程引导与讨论,还设有实践问题思考。与传统实践教材相比,本书不仅在实践教学内容上包含了核心智能计算机视觉技术,而且在内容组织上采用了阶梯式结构。本书的内容编排与理论课程教材一致,可在理论课程教材的教学引导下使用本书进行实践。更重要的是,本书在编写上采用了思维引导式的逻辑培养方法,将人才培养中的思维能力培养融入实践教学,有利于培养创新型人才。
本书提供如下资源:
● 本书源代码;
● 本书思维导图;
● 异步社区7天VIP会员。
要获得以上资源,您可以扫描下方二维码,根据指引领取。
作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。
当您发现错误时,请登录异步社区(https://www.epubit.com),按书名搜索,进入本书页面,点击“发表勘误”,输入错误信息,点击“提交勘误”按钮即可(见下图)。本书的作者和编辑会对您提交的错误信息进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。
我们的联系邮箱是contact@epubit.com.cn。
如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。
如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给本书的责任编辑(shejie@ptpress.com.cn)。
如果您所在的学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。
如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接通过邮件发送给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。
“异步社区”(www.epubit.com)是由人民邮电出版社创办的IT专业图书社区,于2015年8月上线运营,致力于优质内容的出版和分享,为读者提供高品质的学习内容,为作译者提供专业的出版服务,实现作者与读者在线交流互动,以及传统出版与数字出版的融合发展。
“异步图书”是异步社区策划出版的精品IT图书的品牌,依托于人民邮电出版社在计算机图书领域多年的发展与积淀。异步图书面向IT行业以及各行业使用IT的用户。
在实践过程中,我们将讲述怎样安装典型的软件,构建实践平台,开展实践教学,这里以Windows 10操作系统为例。实践环境配置如下。
1)编程语言及环境类软件:Python 3.7、PyCharm、Anaconda 3。
2)其他软件及第三方库:Sklearn(Scikit-learn 0.22.1)、Scikit-image 0.17.2、Matplotlib 3.3.1、SciPy 1.5.2、NumPy 1.19.1、opencv-python 4.4.0.42、Pillow 7.2.0。
3)深度学习框架软件:TensorFlow 1.14,PyTorch 0.8及以上。
应该说明的是,所安装的编程语言及环境类软件和第三方库在各章的实践环节中通用,图像处理的编程语言基于Python 3.7,图像处理的第三方库包括OpenCV、Skimage和PIL。
同时,在深度学习的实践环节中,可以采用深度学习框架TensorFlow,也可以采用深度学习框架PyTorch,学生可以根据需要自行决定。在本书的示例代码中,我们基于PyTorch对编程实例进行讲解。
其中对于编程环境,我们采用的是PyCharm编程环境软件及Anaconda环境设置软件。类似的编程环境软件还有Xcode等,感兴趣的学生可以自行尝试。
下面将阐述实践环境的搭建与环境测试过程。
首先在其官方网站下载Anaconda软件包,然后安装软件,如图0-1所示。
图0-1 下载Anaconda软件包
按照安装向导的提示逐步安装,如图0-2所示。
图0-2 Anaconda安装过程
需要注意的是,安装过程中需要勾选建立环境变量的选项,如图0-3所示。
图0-3 安装时勾选建立环境变量的选项
安装完毕后,按照以下方法测试是否安装成功:打开命令行终端,输入命令“conda –version”,如果正确显示Conda的版本号,说明安装成功。
在实验软件安装与管理过程中,需要创建虚拟环境。我们利用Anaconda软件创建虚拟环境。在Anaconda中,虚拟环境可以通过Windows命令行终端或利用Anaconda的图形化工具创建,这里通过Windows命令行终端来创建。创建虚拟环境时需要给定名称,假设虚拟环境为envname。一般在创建虚拟环境的同时在其中安装Python语言,假设指定安装Python 3.7,命令如下:
conda create –n envname python=3.7
虚拟环境创建成功后,需要激活才能使用。具体方法是在创建虚拟环境后,直接在Windows命令行终端输入以下命令:
activate envname
在激活状态下,可以在虚拟环境中安装实验所需要的软件。另外,输入deactivate命令可以关闭虚拟环境。除此之外,需要强调的是,在虚拟环境中安装软件之前,一般需要添加软件安装的镜像源,学生可以自行在网上查阅有效镜像源添加的内容。
首先进入其官方网站,学生可以根据自己的操作系统版本选择对应的安装包(注意,这里需要选择Community版)。
软件包下载完成后启动安装,进入安装向导,如图0-4和图0-5所示。
图0-4 PyCharm安装向导界面
图0-5 PyCharm安装向导的界面
按照安装向导的提示选择安装路径及安装的选项,其中关联的文件类型选择.py,版本类型选择64位,如图0-6所示。按照安装向导的提示继续安装,直到安装完成。
图0-6 选择关联文件类型的界面
1)安装NumPy。打开“开始”菜单,利用cmd命令打开命令行终端,在激活虚拟环境的情况下,利用如下命令安装NumPy:conda install numpy=1.19.1。
2)安装Scikit-learn。打开“开始”菜单,利用cmd命令打开命令行终端,在激活虚拟环境的情况下,利用如下命令安装Scikit-learn:conda install scikit-learn=0.22.1。
3)安装SciPy。打开“开始”菜单,利用cmd命令打开命令行终端,在激活虚拟环境的情况下,利用如下命令安装SciPy:conda install scipy=1.5.2。
4)安装 Matplotlib。打开“开始”菜单,利用cmd命令打开命令行终端,在激活虚拟环境的情况下,利用如下命令安装Matplotlib:pip install matplotlib,如图0-7所示。
图0-7 Matplotlib安装的界面
5)安装 Pillow。打开“开始”菜单,利用cmd命令打开命令行终端,在激活虚拟环境的情况下,利用如下命令安装Pillow:pip install pillow。
6)安装Sklearn。打开“开始”菜单,利用cmd命令打开命令行终端,在激活虚拟环境的情况下,利用如下命令安装Sklearn:pip install sklearn,如图0-8所示。
图0-8 Sklearn安装的界面
7)安装OpenCV。打开“开始”菜单,利用cmd命令打开命令行终端,在激活虚拟环境的情况下,利用如下命令安装OpenCV:pip install opencv-python,如图0-9所示。
图0-9 OpenCV安装的界面
8)在安装Skimage时,需要安装Scikit-image软件包。打开“开始”菜单,利用cmd命令打开命令行终端,在激活虚拟环境的情况下,利用如下命令安装Scikit-image软件包:pip install scikit-image,如图0-10所示。
图0-10 Scikit-image安装的界面
如果在安装软件包时遭遇下载失败,那么可以考虑通过镜像获取软件源。例如,对于清华源镜像和bioconda,可以采用如下命令加入镜像:
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --set show_channel_urls yes conda config --add channels bioconda
如果有必要,可以通过以下命令移除所增加的镜像源:conda config --remove-key channels,并进一步根据需要添加新的镜像源。另外,在安装包安装过程中可以查看已经设置好的镜像源:conda config --show channels,相关命令可以通过查询网络资源获取。
(1)安装 TensorFlow
在“开始”菜单,利用cmd命令打开命令行终端,如果虚拟环境已经安装好,先激活虚拟环境,然后安装TensorFlow。
安装TensorFlow的GPU版本,可以采用以下命令:
conda install TensorFlow-gpu=1.14.0
安装TensorFlow的CPU版本,可以采用:
conda install TensorFlow=1.14.0
进一步,在命令行终端查看软件包的安装情况:conda list,结果如图0-11所示。
图0-11 安装软件包的列表显示结果
(2)TensorFlow实践环境设置
首先启动PyCharm,创建一个项目。然后在“File”菜单中,利用“Settings”选项,打开“Settings”对话框,选择Project Interpreter项目,并进行相应设置,如图0-12所示。
a)
b)
图0-12 实践环境设置
接着,在Python Interpreter下拉列表框中选择“Show All”选项,显示现有虚拟环境选项,如图0-13所示。
图0-13 显示现有虚拟环境选项界面
再选择已经存在的虚拟环境,如图0-14所示。进一步通过路径查找方式,设置当前虚拟环境的路径,如图0-15所示。
图0-14 选择已经存在的虚拟环境的界面
图0-15 设置现有虚拟环境路径的界面
虚拟环境设置成功后,PyCharm会自动在工程中使用虚拟环境中的软件包,如图0-16所示。
图0-16 虚拟环境设置成功后的界面
对于已经安装的TensorFlow,可以采取以下方法进行测试。首先,启动PyCharm。然后,在PyCharm环境中,通过“File”菜单的“New”选项创建一个项目,再编辑下列代码,如图0-17所示,进一步利用上述步骤进行虚拟环境的设置,之后观察运行结果。
图0-17 PyCharm环境下TensorFlow工程测试代码
(1)安装 PyTorch
首先,如果计算机还未安装CUDA,则先安装与机器显示适配器相对应版本的CUDA软件。具体来说,根据机器显示适配器的条件,在其官方网站找到即将安装的PyTorch软件包,如图0-18所示。
图0-18 PyTorch设置环境
利用cmd命令打开命令行终端,利用以下命令激活环境:activate envname。假设安装的PyTorch对应CUDA的版本为10.2,那么安装PyTorch软件的命令为
conda install PyTorch torchvision cudatoolkit=10.2
那么,如何确定已经成功安装PyTorch软件包呢?
可以采用以下方法进行测试。首先,利用cmd命令打开命令行终端,利用“activate envname”命令激活环境。然后,在虚拟环境中分别输入以下命令。
1)命令1:import torch。
2)命令2:import torchvision。
3)命令3:torch.cuda.is_available()。
如果执行命令1和命令2后都出现错误,并且执行命令3后得到返回值True,如图0-19所示,说明PyTorch软件包还未安装成功,可以查找原因并重新安装。
图0-19 PyTorch安装后环境测试
(2)PyTorch实践环境设置
PyTorch实践环境的设置步骤与TensorFlow类似:先启动PyCharm,创建一个项目;然后在“File”菜单中,利用“Settings”选项打开“Settings”对话框,选择“Project Interpreter”选项并进行设置。不同之处在于PyTorch实践环境设置时所关联的虚拟环境中,需要提前安装好PyTorch软件包。
具体来说,如果PyTorch软件已经成功安装到指定的环境中,为了对PyTorch实践环境进行设置,可利用cmd命令打开命令行终端,利用以下命令激活环境:activate envname。然后参考前面TensorFlow的实践环境设置过程,关联虚拟环境,运行项目。
目前,随着深度学习技术的不断研究和进步,为了在学习过程中更有效地执行反向传播,研究者们致力于实现梯度的高效计算。近年来研究中逐步出现了一些深度学习框架,如Theano、Caffe、TensorFlow、PyTorch、PaddlePaddle、Chainer 和MXNet 等。下面以TensorFlow和PyTorch为例,介绍深度学习框架。
(1)TensorFlow的发展历程
2015年11月,Google公司针对人工智能研究领域的实际需求,对深度学习基础架构DistBelief进行了深入的改进,开发了一款面向应用开发的深度学习框架系统TensorFlow。该系统是TensorFlow的初始版本,主要应用于图像识别等领域。该版本可以应用于小规模的单台计算机设备,以及有多台设备的中心服务器环境。随着技术的不断进步,近年来出现了一系列基于TensorFlow框架的产品。
2016年4月,Google发布了TensorFlow 0.8,该版本支持分布式功能,并在原来初始版本的基础上做了大量的功能更新。从性能角度来看,它已经可以支持100个GPU同时工作,强大的分布式处理功能使其可以有效应用于人工智能产业,产生巨大的影响。
2016年6月,TensorFlow升级到0.9版本,开始支持iOS,这一功能的提升意味着人工智能技术应用于移动端成为现实。
2017年1月,Google公司发布了 TensorFlow 1.0.0-alpha,随后又发布了TensorFlow 1.0.0-rc0版本。TensorFlow 1.0 为TensorFlow性能的进一步完善奠定了基础。
2018年3月,Google公司发布了TensorFlow 1.7.0,在重大改进中集成了NVIDIA的Tensor RT软件包,从而实现了对GPU硬件计算环境的高度优化,在优化速度方面达到了提速8倍的效果。
2018年7月,Google公司发布了TensorFlow 1.9。该版本支持多种平台,包括CPU、GPU、TPU,并支持各种硬件环境,如桌面设备、服务器集群、移动设备和边缘设备等。此外,TensorFlow 1.9还支持通过Java API进行开发,适用于网页环境的应用。在此版本中,还对Keras软件包进行了更新,支持多人人脸识别和特征检测等应用项目。同时,TensorFlow 1.9与TensorFlow.js 1.0框架结合使用,能够在JavaScript中运行浏览器环境下的智能识别算法。
2019年10月,Google公司推出了TensorFlow 2.0。该版本在软件框架上进行了重大改进,特别强调了使用的简单性、高效性和易用性。其中一个显著的特性是能够与Keras无缝集成,使得构建模型变得更加轻松。TensorFlow 2.0还确保了模型能够在多个平台上稳定部署,同时提供了强大的实践工具。此外,该版本简化了API,使其更加易于开发和使用。TensorFlow 2.0的发布标志着TensorFlow进入了新的发展阶段。
在近些年的发展中,TensorFlow不断推陈出新。这些不断更新的新框架在并行计算、任务调度、复杂任务处理等方面进行了优化,使TensorFlow成为更加智能和可扩展的机器学习框架。
(2)TensorFlow使用基础
TensorFlow在深度学习模型处理时采取“定义”与“执行”过程相分离的机制,即先定义网络图,然后将数据注入网络中,再进行执行(训练或者预测)。TensorFlow采用网络图来计算任务,运行任务的上下文被称为会话(Session),即网络图在Session中运行,使用张量(Tensor)存放数据,对于网络模型中可优化的网络参数使用变量(Variable)表示。
使用TensorFlow时,首先需要建立一个数据流图,然后将自己定义的数据以张量的形式参与数据流图计算。这个数据流图与机器学习中的学习模型相对应。在数据流图定义时给定框架形式,在训练时才需要将训练数据注入并进行计算,以便不断优化网络的参数。
在TensorFlow中,张量是一个重要的概念,有多种类型。
● 零阶张量,即纯量或标量,例如[5]。
● 一阶张量,即一维向量形式,例如[3, 8, 1]。
● 二阶张量,即矩阵形式张量,例如[65, 4, 98],[2, 4, 7],[5, 2, 7]
。
此外,还可以定义更高阶的张量。
下面阐述TensorFlow使用过程中的基本问题。
1)数据交互问题。深度学习模型以网络图的框架形式表示,在获取数据后动态执行(训练或者预测)。而网络图在注入数据后进行工作(训练或者预测)。注入数据(Feed)或者取回计算结果(Fetch)的过程,称为数据交互,即数据集与学习模型之间的数据交互过程。针对不同的数据集,数据交互如图0-20所示。
图0-20 学习模型与数据集的交互示意图
2)Session的创建与使用。在TensorFlow中创建及使用Session时,涉及的基本概念如下。
● 张量:在网络图中,用于容纳运算数据的容器。
● 变量:在深度学习模型中定义的、用于网络性能优化的可调变量。例如,卷积神经网络的卷积核的权重、深度神经网络中的权重和偏置等。
● 占位符:输入变量的载体。例如,定义函数中的参数。
● 图中的节点操作(operation,op):用于描述计算图中的单步计算活动,一个op获得0个或者多个张量,执行计算后,产生0个或者多个张量。op用于描述图中张量的运算关系,利用网络图进行操作。
在TensorFlow中,数据流图中的op在执行之前,必须先创建Session对象。创建Session对象时,有三个可选参数,分别介绍如下。
● target:表示是否在分布环境中运行会话,在非分布式环境中使用Session对象时,该参数默认为空。
● graph:指定Session对象中加载的Graph对象,如果不指定则默认加载当前数据流图,但是如果有多个图,需要传入加载的图对象。
● config:Session对象的一些配置信息,CPU或GPU使用上的限制及优化设置。例如,allow_soft_placement选项可以把不适合在 GPU 上运行的 op 全部放到 CPU 上运行;gpu_options.allow_growth允许逐步增长 GPU 显存使用量。
例如,创建一个Session对象,采用默认数据流图:
sess = tf.Session() sess = tf.Session(graph=tf.get_default_graph)
又如,创建一个Session对象,然后运行Session,最后关闭Session:
a=tf.constant([3,5,7,1]) b=tf.constant([2,4,6,8]) theresult=a+b sess=tf.Session () print (sess.run(theresult)) sess.close
3)Session的数据注入机制。我们常采用数据字典方式来实现数据的注入功能,如创建占位符,然后在数据字典(例如feed_dict)中为占位符注入数据,再传入数据流图中。
x= tf.placeholder(dtype=tf.float32) y= tf.placeholder(dtype=tf.float32) sub = x-y product = x*y with tf.Session() as sess: sess.run(tf.global_variables_initializer()) print(' x + y = {0}'.format(sess.run(sub,feed_dict={x:3,y:4}))) print(' x * y = {0}'.format(sess.run(product,feed_dict={x:3,y:4})))
(1)PyTorch的发展历程
PyTorch的工作流程与Python的科学计算库NumPy非常相似,开发者易于上手和使用。Torch深度学习框架的历史可以追溯到2002年,最初它是使用Lua语言开发的。鉴于Python在当时已经成为机器学习领域的主流语言,Facebook决定将Torch的Lua语言描述转换为Python语言。
2016年,Facebook发布了PyTorch初始版本。之后,PyTorch因其提供易用API、具有方便构建复杂计算图的能力,以及可以与Python数据科学栈有效结合而受到研究者的广泛欢迎。PyTorch还提供了构建动态计算图和修改计算图等功能,这些特性使得它在学术界和工业界得到了广泛应用。
PyTorch经历了以下发展阶段。
1)PyTorch 0.1.1,PyTorch与Torch共享底层的C语言描述的API库。同时,在前端代码的组织上,PyTorch也借鉴了Torch的张量和模块等概念。
2)PyTorch 0.2.0,改进了并行计算和异构计算(CPU、GPU混合)方面的性能,引入了多进程计算功能,并逐渐集成CuDNN的GPU深度学习计算库。例如,在PyTorch 0.2.0中实现了高阶导数、分布式计算等功能。
3)PyTorch 0.3.0,支持更多的损失函数和优化器,同时在计算性能的表现上有了很大的提升。
4)PyTorch 0.4.0,对分布式计算的支持更加完善,方便了用户的使用,也增加了对Windows操作系统的支持,实现了张量和变量的合并。
5)PyTorch 1.0,在分布式计算方面开始对不同的后端有了完善的支持,包括MPI和NCCL等。在即时编译器方面也新增了许多功能,可以把PyTorch的动态计算图编译为静态计算图,以方便模型的部署。加强了对C++前端的支持,有效提高了模型的运行效率。此外,基于PyTorch Hub,可以获得一系列预训练的深度学习模型,包括计算机视觉、自然语言处理、生成式模型和音频模型等,方便用户构建自定义模型和复现深度学习模型。
6)PyTorch 1.1,支持TensorBoard对张量的可视化功能。
7)PyTorch 1.2,增强了TorchScript的功能,同时增加了Transformer模块,也增加了对视频、文本和音频训练数据载入的支持。
8)PyTorch 1.3,增加了移动端的处理,以及对模型量化功能的支持,引入了新的JIT编译器,并进一步改进了分布式训练质量。
9)PyTorch 1.4,支持C++ API,并进一步改进了分布式训练质量。
10)PyTorch 1.5,支持同步和异步Tensor传输,引入了TensorPipe。
11)PyTorch 1.6,支持自动混合精度训练。
12)PyTorch 1.7,支持CUDA 10.2,改进了分布式训练质量。
13)PyTorch 1.8,支持生产级PyTorch Script。
14)PyTorch 1.9,支持TensorBoard 2.0。
(2)PyTorch使用基础
PyTorch的应用对象主要有两类:利用GPU进行高性能计算开发者及追求高灵活性深度学习计算的研究者。PyTorch 基于动态计算图,适合动态神经网络结构任务,能够满足各类任务的高性能计算需求。
1)PyTorch 支持自动求导功能。PyTorch 提供了强大的自动求导功能,只需要定义forward 函数、backward函数便可以自动计算导数。
2)PyTorch张量。PyTorch 张量在概念上与 NumPy 数组相同,提供很多在张量上运行的功能。区别在于,NumPy数组可以跟踪计算图,也可用作科学计算的通用工具,但不能利用 GPU 来加速其数值计算,而PyTorch 张量可以利用 GPU 加速计算。
3)PyTorch的nn模块。PyTorch 中的nn包,类似TensorFlow中的Keras、TensorFlow- Slim或者TFLearn 之类软件包,在构建神经网络时提供更高层次的抽象。nn包不仅定义和处理神经网络层,还定义了一组用于神经网络训练的损失函数。
4)PyTorch 中的优化器。PyTorch 中的optim软件包抽象了优化算法的思想,并提供了常用优化算法。例如,可以使用复杂的优化器(如Adam)来训练神经网络。
5)PyTorch 中常见的类。torch.Tensor类支持多维数组,支持backward的自动求导操作,也可以存储张量的梯度。神经网络模块nn.Module以封装参数的方式工作,实现参数移动、导出、加载等功能。nn.Parameter类可以自动注册参数。
基于Python的图像编程经常结合第三方软件包进行,常见的第三方软件包有Skimage、PIL和OpenCV。本书的实践内容部分采用Python作为描述语言,对与Skimage、PIL和OpenCV结合的图像处理算法进行深度讨论。
Skimage包是一组图像处理算法的集合,由志愿者社区团队用Python编程语言实现,并遵循BSD开源许可证。目前它已经成为图像处理的理想工具包。它提供了一个高效的图像算法库的用户接口函数,能够满足研究人员的需求,具有实际的应用价值。它对Scipy.ndimage进行了扩展,提供了更多的图像处理功能。Skimage包由许多子模块组成,各个子模块分别提供不同的功能,主要子模块如下。
● 读取、保存和显示图像的模块io。
● 颜色空间变换模块color。
● 图像增强及边缘检测模块filters。
● 基本图形绘制模块draw。
● 几何变换模块transform。
● 形态学处理模块morphology。
● 图像强度调整模块exposure。
● 特征检测与提取模块feature。
● 图像属性的测量模块measure。
● 图像分割模块segmentation。
● 图像恢复模块restoration。
● 应用功能模块util。
关于Skimage软件包的安装方法,已经在0.1节中进行了阐述。下面阐述如何利用Skimage对图像进行打开及显示处理。
首先,读取和处理图像时需要用到Skimage的io模块,可以使用头文件导入io模块:from Skimage import io。然后,利用io模块获取图像信息并显示图像。
【例0-1】利用Skimage读取和显示图像。
from skimage import io import numpy as np image=io.imread("d:/test/img.jpg") io.imshow(image) io.show()
还可以获取图像的基本信息:
from skimage import io image=io.imread("d:/test/img.jpg") io.imshow(image) io.show() #显示图像 print(type(image)) #打印图像类型信息 print(image.shape) #打印图像尺寸信息 print(image.size) #打印图像总像素个数信息 print(image.max()) #打印图像像素最大值信息 print(image.min()) #打印图像像素最小值信息 print(image mg.mean()) #打印图像像素平均灰度信息
(1)PIL简介
作为Python的官方图像处理库,PIL有着悠久的历史,但它只支持Python 2.7及之前的版本。随着Python 3的推出,出现了一个移植到Python 3的库,名为Pillow。Pillow支持Python 3,现在已经发展成为一个功能更加强大的图像处理库。
因此,对于Python 3.x的用户来说,需要安装Pillow来处理图像。关于Pillow软件包的安装方法,正如0.1节所述,在虚拟环境激活的情况下,只须在虚拟环境中运行以下pip命令即可完成安装:pip install Pillow。
PIL的主要功能如下。
1)图像归档。用于实现图像归档及图像的批处理任务,如创建缩略图、转换图像格式和打印图像等。
2)图像展示。支持ImageTk模块的PhotoImage、BitmapImage和Windows DIB等GUI框架接口,可以有效实现图像的显示与输出。
(2)PIL的图像处理基本概念
1)通道(channel):每幅图像都由一个或者多个通道数据构成。例如,RGB图像有R、G、B三个数据通道,一般情况下,灰度图像只有一个通道。但是,PIL支持使用单通道数据合成多个通道结果。
2)图像模式(mode):定义了图像的类型和像素的位宽。
● mode为1,表示黑白图像中每个像素使用1位存储。
● mode为L,表示8位像素,每个像素用8位来存储灰度值。
● mode为P,表示8位像素,使用调色板映射到其他模式。
● mode为RGB,表示24位像素,为真彩色。
● mode为RGBA,表示32位像素,为有透明通道的真彩色。
● mode为CMYK,表示32位像素,颜色分离。
● mode为YCbCr,表示24位像素,为彩色视频格式。
● mode为I,表示32位整型像素。
● mode为F,表示32位浮点型像素。
3)图像尺寸:通过size方法可以获得,包括左上角坐标、水平和垂直方向的像素数。例如,一个覆盖800×600像素的图像的长方形表示为(0,0,800,600)。
4)坐标系统:采用笛卡儿像素坐标系,原点位于左上角,坐标值表示像素的左上角位置。例如,坐标(0,0)处的像素的中心实际上位于(0.5,0.5)。
5)滤波器:PIL提供了4种不同的采样滤波器。
● 最近滤波(NEAREST):从输入图像中选取最近的像素进行滤波操作。
● 双线性滤波(BILINEAR):在输入图像的2×2矩阵上进行线性插值。但是,在PIL的当前版本中,该滤波器在进行下采样时使用了固定输入模板,用于固定比例的几何变换。
● 双立方滤波(BICUBIC):在输入图像的4×4矩阵上进行立方插值。
● 平滑滤波(ANTIALIAS):此滤波器只用于改变尺寸和生成缩略图,ANTIALIAS滤波器主要用于下采样。
Pillow包括以下模块。
● Image 模块。
● ImageChops模块。
● ImageColor 模块。
● ImageDraw 模块。
● ImageEnhance 模块。
● ImageFile 模块。
● ImageFilter 模块。
● ImageFont 模块。
● ImageGrab 模块(Windows-only)。
● ImageMath 模块。
● ImageOps 模块。
● ImagePalette 模块。
● ImagePath 模块。
● ImageQt 模块。
● ImageSequence 模块。
● ImageStat 模块。
● ImageTk 模块。
● ImageWin 模块。
● PSDraw 模块。
Image类是一个基本类,图像的基本操作依赖Image类的支持。open是Image类中的常用方法之一,用于打开图像和返回一个Image对象。例如:
ImageObj=Image.open("mytest.jpg")
通过返回的ImageObj对象,可以访问图像属性或者对图像进行处理。
● 从ImageObj.format获取图像的格式,如JPG、JPEG、PPM。
● 从ImageObj.size获取图像的大小尺寸。
● 从ImageObj.mode获取图像的颜色属性,即灰度图或RGB。
● 利用ImageObj.show显示图像。
● 利用ImageObj.save(arg1,arg2)将图像arg1保存为arg2格式的图像文件。
● 利用ImageObj.crop(arg)截取图像的矩形区域,其区域由arg参数给定。
● 利用ImageObj.split将图像按照不同通道分离。例如:
R,G,B= ImageObj.split()
● 利用ImageObj.merge将不同通道R、G、B合并为一个RGB图像。例如:
NewImage = ImageObj.merge("RGB",(R,G,B))
下面阐述如何利用PIL对图像进行打开及显示处理。Image类是PIL中的核心类,提供了open、save等图像基本操作。
【例0-2】利用PIL读取和显示图像。
import os, sys from PIL import Image img = Image.open("dogImg.jpg","r") img.show() xsize,ysize= img.size print(img.size,img.format,img.mode) img.save("test.png",'png') print(type(img)) print(img.size) print(img.mode) print(img.format) print(img.getpixel((0,0)))) img.save("testimage.jpg","JPEG")
其中,size表示图像的宽度和高度(单位为像素);format表示图像的格式,常见的图像格式有JPEG、PNG等;mode表示图像的模式,定义了像素类型和图像深度等,常见的有RGB、HSV等。“L”表示灰度图像,“RGB”表示真彩图像,“CMYK”表示预先压缩的图像。
OpenCV是专为计算机视觉研究和应用开发的开放源代码库。在使用OpenCV进行编程时,虽然Python语言的执行效率不及C和C++,但其具有易于编写的特点。针对速度问题,可以用C或C++编写计算功能部分,然后通过Python接口调用这些功能,这样既兼顾了计算的高效性,又保留了编程的便捷性。
目前,OpenCV开源库的开发更加注重实时操作,其效率已经能够满足工业领域的需求。随着人工智能技术的进步,OpenCV在实际应用中常与其他库如NumPy等结合使用,这使得Python能够有效处理OpenCV的数组结构。因此,我们可以在Python编码的基础上,一方面充分利用OpenCV在视觉处理、自动控制等方面的强大功能,另一方面将这些功能嵌入到基于Python编程的人工智能和深度学习框架软件如TensorFlow、PyTorch等中,这样就可以在深度学习框架中充分发挥OpenCV开源视觉库的作用,满足技术研究及各类应用的需要。
要使用OpenCV与Python结合编程,需要安装OpenCV及Python的相关软件包和其他第三方库。使用不同版本的Python与OpenCV进行图像处理时,算法的编写和处理方法可能会有所不同。这里以OpenCV 4.1和Python 3.7为例展开介绍。
(1)图像类型的支持
常见的图像类型包括二值图像、灰度图像和彩色图像。
● 二值图像:具有0和1两级灰度的图像,如果用灰度对应,0为黑色,1为白色。
● 灰度图像:256个灰度级,灰度范围为[0,255],0为黑色,255为白色。
● 彩色图像:用RGB色彩空间表示时,具有红、绿、蓝三种通道,强度范围为[0,255]。应注意,在OpenCV中色彩空间用BGR表示。
在OpenCV中,cv2.imread()函数用于实现图像的读取,其格式如下:
cv2.imread(filename, flags])
其中,filename是读取图像的文件名(可以指定路径)。不指定路径时,默认读取的图像在当前项目的根目录下。flags 用于指定图像的读取方式,具体参数如下。
● cv2.IMREAD_COLOR:加载彩色图像,并忽略图像的透明信息,默认读入彩色图像。
● cv2.IMREAD_GRAYSCALE:读入灰度图像。
● cv2.IMREAD_UNCHANGED:读入包含透明度通道的图像。
对于这三种常量参数选项,在OpenCV中分别用1、0、−1表示。
(2)OpenCV支持不同色彩空间中图像格式的转换
图像表达与色彩空间紧密相关。常见的色彩空间有RGB和CMYK,RGB以红、绿、蓝为基色,CMYK则以青、品红、黄和黑为基色。此外,HSL和HSV色彩空间也较为常用,它们用色调、饱和度和亮度/色值来表达图像。处理图像时,常常需要在不同色彩空间进行转换。OpenCV提供了对图像在不同色彩空间的转换功能。例如,在读取数字图像后,可以根据需要进行色彩空间的转换:
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) img_hls = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)
这两个函数调用语句将img分别从RGB色彩空间转换为HSV和HLS色彩空间。
应该说明的是,不同于Matplotlib,OpenCV在默认情况下读取的是BGR色彩信息。因此,在实际算法处理时,常需要进行格式的转换:
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
【例0-3】利用OpenCV读取图像并显示图像。
import cv2 img = cv2.imread('mytestimage.jpg') cv2.imshow("Image", img)
【例0-4】利用OpenCV实现通道分离的功能。
在对数字图像进行处理和分析时,经常需要对不同通道的信息进行分离。利用OpenCV,能够容易地实现像素处理和通道分离功能:
import matplotlib.pyplot as plt import cv2 img = cv2.imread('mytestimage.jpg') for i in range(0,3): tmp[i] = img[:, :, i] tmp.imshow(img [:, :, i], cmap = 'gray') plt.show()
其中tmp用于获取读入图像img不同通道的分离结果,以进一步绘制不同通道的分量。
【例0-5】利用OpenCV实现图像的像素处理。
import matplotlib.pyplot as plt import cv2 img = cv2.imread(‘mytestimage.jpg') size = img.shape for i in range(0, size[0]): for j in range(0, size[1]): for k in range(0,3): img[i,j,k]=255 cv2.imshow("Image", img)
本节将介绍深度学习的编程示例,以及深度学习编程框架。接下来将以PyTorch深度学习框架为例,阐述基本编程方法,并通过一个简单的示例程序演示深度学习编程过程。
在深度学习算法程序中,通常包括训练阶段、预测阶段和评估阶段。在本节的编程框架知识讲解中,将重点阐述训练阶段和预测阶段的程序设计。如图0-21所示,训练阶段程序结构主要包括训练主程序、训练数据定义模块、网络模型定义模块、训练参数设置和训练模块,各部分的功能如下。
图0-21 深度学习算法程序结构
1)训练主程序用于提供训练程序入口。
2)训练数据定义模块用于对训练数据进行初始化设置(包括路径位置和数据组织),为训练时取数据做准备。
3)网络模型定义模块用于构建算法模型。
4)训练模块用于网络训练的初始化和训练过程。在初始化时,设置数据载入器、网络优化器及已有模型参数的初始化等;在训练过程中,通过取数据、喂数据和网络的优化控制,实现模型的迭代优化。
5)训练参数设置用于设置训练过程需要的参数,如学习率、epoch、批次等。
如图0-21所示,预测阶段程序结构主要包括预测主程序、预测数据定义模块、网络模型定义模块、预测参数设置和预测模块,各部分的功能如下。
1)预测主程序用于提供预测程序入口。
2)预测数据定义模块用于对预测数据进行初始化设置(包括路径位置和数据组织),为预测取数据做准备。
3)网络模型定义模块用于构建算法模型,该模型与训练阶段构建的模型保持一致。
4)预测参数设置用于设置预测过程需要的参数。
5)预测模块用于网络预测的初始化和预测过程。在初始化时,设置预测数据载入器及用已有模型参数初始化网络等;在预测过程中,通过取数据、喂数据,得到模型的预测结果。
下面以手写数字识别为例来演示深度学习编程,为了规范起见,按照图0-21所述深度学习编程框架进行设计。这里采用LeNet作为编码网络的架构。
(1)训练阶段程序结构
在手写数字识别实例中,训练阶段程序结构主要包括以下几个部分。
1)训练主程序。
import configs.config_loader as cfg_loader from model.LeNet import * from model.training import * from dataset import * def main(): args = cfg_loader.get_config() model = LeNet(in_channels=1, out_channels=1) TrainDataset = MyDataset('train') trainer = Trainer(model, TrainDataset, args) trainer.train_model() if __name__ == '__main__': main()
2)训练数据定义模块。
import torch from torch.utils.data import Dataset import os from PIL import Image from torchvision import transforms class MyDataset(Dataset): def __init__(self, img_dir): self.img_dir = img_dir self.data = [] self.labels = [] for label in range(10): label_dir = os.path.join(img_dir, str(label)) for img_file in os.listdir(label_dir): self.data.append(os.path.join(label_dir, img_file)) self.labels.append(label) self.transform = transforms.Compose( [ transforms.RandomHorizontalFlip(p=0.5), transforms.RandomRotation(30), transforms.ToTensor() ] ) def __len__(self): return len(self.data) def __getitem__(self, index): img_path = self.data[index] img = Image.open(img_path).convert('L') img = self.transform(img) label = self.labels[index] return img, label
3)网络模型定义模块。此模块利用网络LeNet构建算法模型。
import torch import torch.nn.functional as F import torch.nn as nn class LeNet(nn.Module): def __init__(self, in_channels, out_channels): super(LeNet, self).__init__() self.conv1 = nn.Conv2d(1, 10, kernel_size=5) self.conv2 = nn.Conv2d(10, 20, kernel_size=5) self.fc1 = nn.Linear(20 * 4 * 4, 500) self.fc2 = nn.Linear(500, 10) def forward(self, x): x = F.relu(self.conv1(x)) x = F.max_pool2d(x, 2) x = F.relu(self.conv2(x)) x = F.max_pool2d(x, 2) x = x.view(-1, 20 * 4 * 4) x = F.relu(self.fc1(x)) x = self.fc2(x) return F.log_softmax(x, dim=1)
4)训练模块。
from __future__ import division import torch import numpy as np from torch.utils import data class Trainer(object): def __init__(self, model, TrainDataset, args): self.args = args self.model = model if torch.cuda.is_available(): self.model.cuda() self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") self.Trainloader = data.DataLoader(dataset=TrainDataset, batch_size=args.batch_size, shuffle=False, num_workers=0) self.criterion = torch.nn.CrossEntropyLoss() self.optimizer = torch.optim.Adam(lr=0.003, params=model.parameters()) self.epochs = args.epochs self.start_epoch = args.start_epoch def train_model(self): self.model.train() for epoch in range(self.start_epoch, self.epochs): for i, (x, y) in enumerate(self.Trainloader): x, y = x.to(self.device), y.to(self.device) self.optimizer.zero_grad() pre_y = self.model(x) loss = self.criterion(pre_y, y) loss.backward() self.optimizer.step() if i%30 == 0: print(r'训练的代数:{} [当前代完成进度:{} / {} ({:.0f}%)],当前损失函数值: {:.6f}'.format(epoch, i * len(x),len(self.Trainloader.dataset), 100. * i / len(self.Trainloader), loss.item())) torch.save(self.model, 'LeNet.pth')
5)训练参数设置。
import configargparse def config_parser(): parser = configargparse.ArgumentParser(description='DigitRecognition') parser.add_argument('--batch_size', type=int, default=512, help='path to latest checkpoint') parser.add_argument('--start_epoch', type=int, default=0, help='manual epoch number (useful on restarts)') parser.add_argument('--epochs', type=int, default=30, help='total number of training rounds') return parser def get_config(): parser = config_parser() cfg = parser.parse_args() return cfg
(2)预测阶段程序结构
预测程序设计一般由以下几个部分组成。
1)预测主程序。
import configs.config_loader as cfg_loader from model.LeNet import * from model.training import * from dataset import * from torchvision import transforms def test(): model = torch.load('LeNet.pth') img = Image.open('test/0/3.jpg') pred = transforms.ToTensor()(img) pred = torch.unsqueeze(pred, 0) model.cpu() pred = model(pred) _, predicted = torch.max(pred, 1) print("预测的数字是:", predicted.item()) if __name__ == '__main__': test() eval()
2)预测数据定义模块。
import torch from torch.utils.data import Dataset import os from PIL import Image from torchvision import transforms class MyDataset(Dataset): def __init__(self, img_dir): self.img_dir = img_dir self.data = [] self.labels = [] for label in range(10): label_dir = os.path.join(img_dir, str(label)) for img_file in os.listdir(label_dir): self.data.append(os.path.join(label_dir, img_file)) self.labels.append(label) self.transform = transforms.Compose( [ transforms.RandomHorizontalFlip(p=0.5), transforms.RandomRotation(30), transforms.ToTensor() ] ) def __len__(self): return len(self.data) def __getitem__(self, index): img_path = self.data[index] img = Image.open(img_path).convert('L') img = self.transform(img) label = self.labels[index] return img, label
3)网络模型定义模块。具体参见训练阶段的网络模型定义模块。
4)预测模块。
def eval(): model = torch.load('LeNet.pth') model.eval() correct = 0 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") test_dataset = MyDataset('test') test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=512, shuffle=True) with torch.no_grad(): for x, y in test_loader: x, y = x.to(device), y.to(device) pre_y = model(x) pre_y = torch.argmax(pre_y, dim=1) t = (pre_y == y).long().sum() correct += t correct = correct.data.cpu().item() correct = 1. * correct / len(test_loader.dataset) print('在测试集上的预测准确率:{:0.2f}%'.format(100 * correct))
5)预测参数设置。
import configargparse def config_parser(): parser = configargparse.ArgumentParser(description='Parathdetect') parser.add_argument('--batch_size', type=int, default=512, help='path to latest checkpoint') parser.add_argument('--start_epoch', type=int, default=0, help='manual epoch number (useful on restarts)') parser.add_argument('--epochs', type=int, default=30, help='total number of training rounds') return parser def get_config(): parser = config_parser() cfg = parser.parse_args() return cfg