IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> OpenCV进阶篇 -> 正文阅读

[人工智能]OpenCV进阶篇

sss

OpenCV进阶篇

第10章 模板匹配

模板匹配是一种最原始、最基本的识别方法,可以在原始图像中寻找特定图像的位置。模板匹配经常应用于简单的图像查找场景中,例如,在集体合照中找到某个人的位置。本章将介绍如何利用OpenCV实现模板匹配。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Qunwp3k-1639056667651)(OpenCV进阶篇.assets/image-20211122082012339.png)]

10.1 模板匹配方法

模板是被查找目标的图像,查找模板在原始图像中的哪个位置的过程就叫模板匹配。OpenCV提供的matchTemplate()方法就是模板匹配方法,其语法如下:

 result = cv2.matchTemplate(image, templ, method, mask)

参数说明:

image:原始图像。

templ:模板图像,尺寸必须小于或等于原始图像。

method:匹配的方法,可用参数值如表10.1所示。

表10.1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yTgvDiwC-1639056667652)(OpenCV进阶篇.assets/image-20211122082047351.png)]

匹配方法的参数值

mask:可选参数。掩模,只有cv2.TM_SQDIFF和cv2.TM_CCORR_NORMED支持此参数,建议采用默认值。

返回值说明:

result:计算得出的匹配结果。如果原始图像的宽、高分别为W、H,模板图像的宽、高分别为w、h,result就是一个W-w+1列、H-h+1行的32位浮点型数组。数组中每一个浮点数都是原始图像中对应像素位置的匹配结果,其含义需要根据method参数来解读。
在模板匹配的计算过程中,模板会在原始图像中移动。模板与重叠区域内的像素逐个对比,最后将对比的结果保存在模板左上角像素点索引位置对应的数组位置中。计算过程如图10.1所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q6lurtRV-1639056667652)(OpenCV进阶篇.assets/image-20211122082229394.png)]

? 图10.1 模板在原始图像中移动并逐个匹配

使用cv2.TM_SQDIFF(平方差匹配)方法计算出的数组格式如下(其他方法计算出的数组格式相同,仅数值不同):

 [[0.10165964 0.10123613 0.1008469  ... 0.10471864 0.10471849 0.10471849]
  [0.10131165 0.10087635 0.10047968 ... 0.10471849 0.10471834 0.10471849]
  [0.10089004 0.10045089 0.10006084 ... 0.10471849 0.10471819 0.10471849]
  ...
  [0.16168603 0.16291814 0.16366465 ... 0.12178455 0.12198001 0.12187888]
  [0.15859096 0.16000605 0.16096526 ... 0.12245651 0.12261643 0.12248362]
  [0.15512456 0.15672517 0.15791312 ... 0.12315679 0.1232616  0.12308815]]

模板将原始图像中每一块区域都覆盖一遍,但结果数组的行、列数并不等于原始图像的像素的行、列数。假设模板的宽为w,高为h,原始图像的宽为W,高为H,如图10.2所示。
模板移动到原始图像的边缘之后就不会继续移动了,所以模板的移动区域如图10.3所示,该区域的边长为“原始图像边长-模板边长+1”,最后加1是因为移动区域内的上下、左右的2个边都被模板覆盖到了,如果不加1会丢失数据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-egJKRmNg-1639056667653)(OpenCV进阶篇.assets/image-20211122085327267.png)]

? 图10.2 模板和原始图像的宽、高

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vEnbCYJW-1639056667653)(OpenCV进阶篇.assets/image-20211122085350483.png)]

? 图10.3 模板移动的范围

10.2 单模板匹配

匹配过程中只用到一个模板场景叫单模板匹配。原始图像中可能只有一个和模板相似的图像,也可能有多个。如果只获取匹配程度最高的那一个结果,这种操作叫作单目标匹配。如果需要同时获取所有匹配程度较高的结果,这种操作叫作多目标匹配。

10.2.1 单目标匹配

单目标匹配只获取一个结果即可,就是匹配程度最高的结果(如果使用平方差匹配,则为计算出的最小结果;如果使用相关匹配或相关系数匹配,则为计算出的最大结果)。本节以平方差匹配为例介绍。
matchTemplate()方法的计算结果是一个二维数组,OpenCV提供了一个minMaxLoc()方法专门用来解析这个二维数组中的最大值、最小值以及这2个值对应的坐标,minMaxLoc()方法的语法如下:

 minValue, maxValue, minLoc, maxLoc = cv2.minMaxLoc(src, mask)

参数说明:

src:matchTemplate()方法计算得出的数组。

mask:可选参数,掩模,建议使用默认值。

返回值说明:

minValue:数组中的最小值。

maxValue:数组中的最大值。

minLoc:最小值的坐标,格式为(x, y)。

maxLoc:最大值的坐标,格式为(x, y)。
平方差匹配的计算结果越小,匹配程度越高。minMaxLoc()方法返回的minValue值就是模板匹配的最优结果,minLoc就是最优结果区域左上角的点坐标,区域大小与模板大小一致。

【实例10.1】 为原始图片中匹配成功的区域绘制红框。
将图10.4作为模板,将图10.5作为原始图像,使用cv2.TM_SQDIFF_NORMED方式进行模板匹配,在原始图像中找到与模板一样的图案,并在该图案上绘制红色方框。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rar15A3x-1639056667654)(OpenCV进阶篇.assets/image-20211122085451979.png)]

? 图10.4 模板

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gEC0AtkX-1639056667654)(OpenCV进阶篇.assets/image-20211122085539068.png)]

? 图10.5 原始图片
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2A0hJfRU-1639056667655)(OpenCV进阶篇.assets/image-20211122085602382.png)]

上述代码的运行结果如图10.6所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nO5y7aFc-1639056667655)(OpenCV进阶篇.assets/image-20211122085749850.png)]

? 图10.6 模板匹配的效果

在许多综艺节目里,导演组给选手们一幅图像,让选手在指定区域内寻找图像中的某一静物。为了增加游戏难度,导演组可能会让选手们从2个或者多个相似的场景中选择最佳的匹配结果。接下来,使用模板匹配的相应方法模拟这个游戏。

【实例10.2】 从2幅图像中选择最佳的匹配结果。
将图10.7作为模板,将图10.8和图10.9作为原始图像,使用cv2.TM_SQDIFF_NORMED方式进行模板匹配,在2幅原始图像中找到与模板匹配结果最好的图像,并在窗口中显示出来。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kAeDADkx-1639056667656)(OpenCV进阶篇.assets/image-20211122085811909.png)]

? 图10.7 模板

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Y2C8Uz6-1639056667656)(OpenCV进阶篇.assets/image-20211122085837681.png)]

? 图10.8 原始图像221

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F0efgzCT-1639056667656)(OpenCV进阶篇.assets/image-20211122085907444.png)]

? 图10.9 原始图像222
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iun44Nij-1639056667657)(OpenCV进阶篇.assets/image-20211122085934422.png)]

上述代码的运行结果如图10.10所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2okGh7GG-1639056667657)(OpenCV进阶篇.assets/image-20211122090014973.png)]

? 图10.10 从2幅图像中选择最佳的匹配结果

网速的提升让容量较大的文件更容易在互联网上传播,最明显结果就是现在用户计算机里被堆满了各种各样的图像文件。
图像文件与其他文件不同,相同内容的图像可能保存在不同大小、不同格式的文件中,这些文件的二进制字节码差别较大,很难用简单的程序识别。在没有高级识别软件的情况下想要找出内容相同的图像就只能一个一个打开用肉眼识别了。
OpenCV能够打破图像文件规格、格式的限制来识别图像内容。

【实例10.3】 查找重复的图像。
图10.11所示的文件夹中有10幅图像,这些图像不仅有JPG格式的,还有PNG格式的,而且这些图像的分辨率也各不相同。接下来将编写一个程序,在该文件夹中找出哪些是重复的照片。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bny5k3Mh-1639056667657)(OpenCV进阶篇.assets/image-20211122090116485.png)]

? 图10.11 文件夹中的所有照片文件
想要解决这个问题,可以使用OpenCV提供的matchTemplate()方法来判断2幅图像的相似度,如果相似度大于0.9,就认为这2幅图像是相同的。
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0MWgYl4D-1639056667658)(OpenCV进阶篇.assets/image-20211122090219888.png)]

上述代码的运行结果如下:

 相同的照片:10.png, 4.jpg,
 相同的照片:2.jpg, 5.jpg, 9.png,

10.2.2 多目标匹配

多目标匹配需要将原始图像中所有与模板相似的图像都找出来,使用相关匹配或相关系数匹配可以很好地实现这个功能。如果计算结果大于某值(例如0.999),则认为匹配区域的图案和模板是相同的。

【实例10.4】 为原始图片中所有匹配成功的图案绘制红框。
将图10.12作为模板,将图10.13作为原始图像。原始图像中有很多重复的图案,每一个与模板相似的图案都需要被标记出来。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sPJngqiS-1639056667658)(OpenCV进阶篇.assets/image-20211122090300666.png)]

? 图10.12 模板

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z3YHi9fS-1639056667658)(OpenCV进阶篇.assets/image-20211122090321173.png)]

? 图10.13 包含重复内容的原始图像
使用cv2.TM_CCOEFF_NORMED方法进行模板匹配,使用for循环遍历matchTemplate()方法返回的结果,找到所有大于0.99的计算结果,在这些结果的对应区域位置绘制红色矩形边框。编写代码时要注意:数组的列数在图像坐标系中为横坐标,数组的行数在图像坐标系中为纵坐标。
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9AHVz1kN-1639056667658)(OpenCV进阶篇.assets/image-20211122090346039.png)]

上述代码的运行结果如图10.14所示,程序找到了3处与模板相似的图案。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z1Eg2Ozz-1639056667659)(OpenCV进阶篇.assets/image-20211122090426271.png)]

? 图10.14 匹配结果
多目标匹配在实际生活中有很多应用场景。例如,统计一条快轨线路的站台总数;同一地点附近有2个地铁站,优先选择直线距离最短的地铁站等。

【实例10.5】 统计一条快轨线路的站台总数。
将图10.15作为模板,图10.16作为原始图像,在原始图像中标记快轨线路各个站台,统计这条快轨线路的站台总数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qnkqoHAG-1639056667659)(OpenCV进阶篇.assets/image-20211122090450906.png)]

? 图10.15 模板

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKvqjLpo-1639056667659)(OpenCV进阶篇.assets/image-20211122090516424.png)]

? 图10.16 原始图像
使用cv2.TM_CCOEFF_NORMED方法进行模板匹配,使用for循环遍历matchTemplate()方法返回的结果,找到所有大于0.99的计算结果,在这些结果的对应区域位置绘制蓝色矩形边框,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PcXPP4GV-1639056667660)(OpenCV进阶篇.assets/image-20211122090545588.png)]

上述代码的运行结果如图10.17所示。
实例10.5第6行中的results包含所有蓝色矩形边框左上角的横、纵坐标。利用这一特点,还可以模拟“同一地点附近有2个地铁站,优先选择直线距离最短的地铁站”这一生活场景,模板如图10.18所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FOlMVoCl-1639056667660)(OpenCV进阶篇.assets/image-20211122090628716.png)]

? 图10.17 统计一条快轨线路的站台总数

【实例10.6】 优先选择直线距离最短的地铁站。
如图10.19所示,坐标为(62, 150)的地点附近有人民广场和解放大路两个地铁站,如何优先选择直线距离最短的地铁站呢?首先将图10.18作为模板,将图10.19作为原始图像,然后在原始图像中标记出这两个地铁站,最后计算并比较坐标为(62, 150)这个地点与这两个地铁站的直线距离。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sZTp2jPj-1639056667660)(OpenCV进阶篇.assets/image-20211122090830746.png)]

? 图10.18 模板

? 图10.19 原始图像
使用cv2.TM_CCOEFF_NORMED方法进行模板匹配,使用for循环遍历matchTemplate()方法返回的结果,找到所有大于0.99的计算结果,在这些结果的对应区域位置绘制蓝色矩形边框,分别计算(62,150)到蓝色矩形边框左上角的距离,用绿色线段标记出直线距离最短的地铁站,代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0DHd3yX7-1639056667660)(OpenCV进阶篇.assets/image-20211122091313117.png)]

上述代码的运行结果如图10.20所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9DG7tnQ-1639056667661)(OpenCV进阶篇.assets/image-20211122091347963.png)]

? 图10.20 优先选择直线距离最短的地铁站

10.3 多模板匹配

匹配过程中同时查找多个模板的操作叫多模板匹配。多模板匹配实际上就是进行了n次“单模板多目标匹配”操作,n的数量为模板总数。【实例10.7】 同时匹配3个不同的模板。
将图10.21~图10.23作为模板,将图10.24(a)作为原始图像。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3YCOPde6-1639056667661)(OpenCV进阶篇.assets/image-20211122091419764.png)]

? 图10.21 模板1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r9yUEVDC-1639056667661)(OpenCV进阶篇.assets/image-20211122091440162.png)]

? 图10.22 模板2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m1bOnojo-1639056667662)(OpenCV进阶篇.assets/image-20211122091458891.png)]

? 图10.23 模板3
每一个模板都要做一次“单模板多目标匹配”,最后把所有模板的匹配结果汇总到一起。“单模板多目标匹配”的过程可以封装成一个方法,方法参数为模板和原始图像,方法内部将计算结果再加工一下,直接返回所有红框左上角和右下角两点横纵坐标的列表。在方法之外,将所有模板计算得出的坐标汇总到一个列表中,按照这些汇总的坐标一次性将所有红框都绘制出来。
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ElkKc33-1639056667662)(OpenCV进阶篇.assets/image-20211122091539261.png)]

上述代码的运行效果如图10.24(b)所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uph18qmS-1639056667662)(OpenCV进阶篇.assets/image-20211122091621151.png)]

? 图10.24 多模板匹配效果
使用多模板匹配能够解决很多生活中的实际问题。例如,一个收费停车场有4个车位,车位上陆续地停放了4辆车,通过多模板匹配,能够知晓这4辆车分别停在了哪个车位上。接下来将模拟这一生活场景。

【实例10.8】 使用多模板匹配让控制台判断4辆车分别停在了哪个车位上。
有4辆车按图10.25~图10.28的顺序陆续驶入停车场,这4辆车停在4个车位上的效果如图10.29所示。将图10.25~图10.28作为模板,将图10.29作为原始图像,使用cv2. TM_CCOEFF_NORMED方式进行模板匹配,在原始图像中找到与4个模板一样的图像后,在控制台上输出这4辆车分别停在了哪个车位上。

说明
在图10.29中,1号车位水平像素的取值范围是0200,2号车位水平像素的取值范围是200433,3号车位水平像素的取值范围是433656,4号车位水平像素的取值范围是656871。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dnthllJ8-1639056667662)(OpenCV进阶篇.assets/image-20211122091712450.png)]

? 图10.25 模板1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yj96pba2-1639056667663)(OpenCV进阶篇.assets/image-20211122091731718.png)]

? 图10.26 模板2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jvSXLJN1-1639056667663)(OpenCV进阶篇.assets/image-20211122091750330.png)]

? 图10.27 模板3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p9XHjoRB-1639056667663)(OpenCV进阶篇.assets/image-20211122091808719.png)]

? 图10.28 模板4

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H2QimGeQ-1639056667663)(OpenCV进阶篇.assets/image-20211122091825529.png)]

? 图10.29 原始图像
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xUmuJe8T-1639056667664)(OpenCV进阶篇.assets/image-20211122091850011.png)]

上述代码的运行结果如下:

 车位编号: 4
 车位编号: 3
 车位编号: 2
 车位编号: 1

上面的结果可以得出以下结论:图10.25所示的车辆停在了4号车位上,图10.26所示的车辆停在了3号车位上,图10.27所示的车辆停在了2号车位上,图10.28所示的车辆停在了1号车位上。

10.4 小结

模板匹配包括单模板匹配和多模板匹配,单模板匹配又包括单目标匹配和多目标匹配。实现这些内容的基础方法就是模板匹配方法,即matchTemplate()方法。其中,重点掌握模板匹配方法的6个参数值。此外,为了实现单目标匹配,除了需要使用模板匹配方法matchTemplate()外,还要使用minMaxLoc()方法,这个方法返回的就是单目标匹配的最优结果。对于多目标匹配,要将它和多模板匹配区分开:多目标匹配只有一个模板,而多模板匹配则有多个模板。

第11章 滤波器

在尽量保留原图像信息的情况下,去除图像内噪声、降低细节层次信息等一系列过程,叫作图像的平滑处理(或图像的模糊处理)。实现平滑处理最常用的工具就是滤波器。通过调节滤波器的参数,可以控制图像的平滑程度。OpenCV提供了种类丰富的滤波器,每种滤波器使用的算法均不同,但都能对图像中的像素值进行微调,让图像呈现平滑效果。本章将介绍均值滤波器、中值滤波器、高斯滤波器和双边滤波器的使用方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vSWZ4Fzw-1639056667664)(OpenCV进阶篇.assets/image-20211122092213657.png)]

11.1 均值滤波器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xl2LLuQr-1639056667664)(OpenCV进阶篇.assets/image-20211122092621030.png)]

? 图11.1 噪声图像
图像中可能会出现这样一种像素,该像素与周围像素的差别非常大,导致从视觉上就能看出该像素无法与周围像素组成可识别的图像信息,降低了整个图像的质量。这种“格格不入”的像素就是图像的噪声。如果图像中的噪声都是随机的纯黑像素或者纯白像素,这样的噪声称作“椒盐噪声”或“盐噪声”。例如如图7.1所示的就是一幅只有噪声的图像,常称为“雪花点”。
以一个像素为核心,其周围像素可以组成一个n行n列(简称n×n)的矩阵,这样的矩阵结构在滤波操作中被称为“滤波核”。矩阵的行、列数决定了滤波核的大小,如图11.2所示的滤波核大小为3×3,包含9个像素;图11.3所示的滤波核大小为5×5,包含25个像素。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A7X4TOAK-1639056667665)(OpenCV进阶篇.assets/image-20211122092605201.png)]

? 图11.2 3×3的滤波核

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pYkMEF6M-1639056667665)(OpenCV进阶篇.assets/image-20211122092712502.png)]

? 图11.3 5×5的滤波核
均值滤波器(也称为低通滤波器)可以把图像中的每一个像素都当成滤波核的核心,然后计算核内所有像素的平均值,最后让核心像素值等于这个平均值。
例如,图11.4就是均值滤波的计算过程。滤波核大小为3×3,核心像素值是35,颜色较深,周围像素值都为110~150,因此可以认为核心像素是噪声。将滤波核中的所有像素值相加,然后除以像素个数,就得出了平均值123(四舍五入取整)。将核心像素的值改成123,其颜色就与周围颜色差别不大,图像就变得平滑了。这就是均值滤波去噪的原理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U0Y6zVrq-1639056667665)(OpenCV进阶篇.assets/image-20211122092745945.png)]

? 图11.4 均值滤波的计算过程
OpenCV将均值滤波器封装成blur()方法,其语法如下:

 dst = cv2.blur(src, ksize, anchor, borderType)

参数说明:

src:被处理的图像。

ksize:滤波核大小,其格式为(高度,宽度),建议使用如(3, 3)、(5, 5)、(7, 7)等宽、高相等的奇数边长。滤波核越大,处理之后的图像就越模糊。

anchor:可选参数,滤波核的锚点,建议采用默认值,可以自动计算锚点。

borderType:可选参数,边界样式,建议采用默认值。

返回值说明:

dst:经过均值滤波处理之后的图像。

【实例11.1】 对花朵图像进行均值滤波操作。
分别使用大小为3×3、5×5和9×9的滤波核对花朵图像进行均值滤波操

作,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iSHWrz2c-1639056667665)(OpenCV进阶篇.assets/image-20211122094217442.png)]

上述代码的运行结果如图11.5所示,从这个结果可以看出,滤波核越大,处理之后的图像就越模糊。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5tDKOosJ-1639056667666)(OpenCV进阶篇.assets/image-20211122092914325.png)]

? 图11.5 图像均值滤波效果

11.2 中值滤波器

中值滤波器的原理与均值滤波器非常相似,唯一的不同就是不计算像素的平均值,而是将所有像素值排序,把最中间的像素值取出,赋值给核心像素。

例如,图11.6就是中值滤波的计算过程。滤波核大小为3×3,核心像素值是35,周围像素值都为110~150。将核内所有像素值按升序排列,9个像素值排成一行,最中间位置为第5个位置,这个位置的像素值为131。不需再做任何计算,直接把131赋值给核心像素,其颜色就与周围颜色差别不大,图像就变得平滑了。这就是中值滤波去噪的原理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ggPwhx8h-1639056667666)(OpenCV进阶篇.assets/image-20211122095233594.png)]

图11.6 中值滤波的计算过程
OpenCV将中值滤波器封装成medianBlur()方法,其语法如下:

 dst = cv2.medianBlur(src, ksize)

参数说明:

src:被处理的图像。

ksize:滤波核的边长,必须是大于1的奇数,如3、5、7等。该方法根据此边长自动创建一个正方形的滤波核。

返回值说明:

dst:经过中值滤波处理之后的图像。

注意
中值滤波器的ksize参数是边长,而其他滤波器的ksize参数通常为(高,宽)。

【实例11.2】 对花朵图像进行中值滤波操作.
分别使用边长为3、5、9的滤波核对花朵图像进行中值滤波操作,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gRpNOjqx-1639056667666)(OpenCV进阶篇.assets/image-20211122095347319.png)]

上述代码的运行结果如图11.7所示,滤波核的边长越长,处理之后的图像就越模糊。中值滤波处理的图像会比均值滤波处理的图像丢失更多细节。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nvL2Le6F-1639056667666)(OpenCV进阶篇.assets/image-20211122095440078.png)]

? 图11.7 图像中值滤波效果

11.3 高斯滤波器

高斯滤波也被称为高斯模糊或高斯平滑,是目前应用最广泛的平滑处理算法。高斯滤波可以很好地在降低图片噪声、细节层次的同时保留更多的图像信息,经过处理的图像呈现“磨砂玻璃”的滤镜效果。
进行均值滤波处理时,核心周围每个像素的权重都是均等的,也就是每个像素都同样重要,所以计算平均值即可。但在高斯滤波中,越靠近核心的像素权重越大,越远离核心的像素权重越小,例如5×5大小的高斯滤波卷积核的权重示意图如图11.8所示。像素权重不同不能取平均值,要从权重大的像素中取较多的信息,从权重小的像素中取较少的信息。简单概括就是“离谁更近,跟谁更像”。
高斯滤波的计算过程涉及卷积运算,会有一个与滤波核大小相等的卷积核。本节仅以3×3的滤波核为例,简单地描述一下高斯滤波的计算过程。
卷积核中保存的值就是核所覆盖区域的权重值,其遵循图11.8的规律。卷积核中所有权重值相加的结果为1。例如,3×3的卷积核可以是如图11.9所示的值。随着核大小、σ标准差的变化,卷积核中的值也会发生较大变化,图11.9仅是一种最简单的情况。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bUJTJIJq-1639056667667)(OpenCV进阶篇.assets/image-20211122095530857.png)]

? 图11.8 5×5的高斯滤波卷积核的权重示意图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4h3BuqBO-1639056667667)(OpenCV进阶篇.assets/image-20211122095559972.png)]

? 图11.9 简化的3×3的卷积核
进行高斯滤波的过程中,滤波核中像素与卷积核进行卷积计算,最后将计算结果赋值给滤波核的核心像素。其计算过程如图11.10所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f24HEXuv-1639056667667)(OpenCV进阶篇.assets/image-20211122095620790.png)]

? 图11.10 像素与卷积核进行卷积计算
在图11.10的计算过程中,滤波核中的每个像素值都与卷积核对应位置的权重值相乘,最后计算出9个值,计算过程如下:

 137 × 0.05  150 × 0.1  125 × 0.05     6.85  15    6.25
 141 × 0.1   35 × 0.4   131 × 0.1   =  14.1  14    13.1
 119 × 0.05  118 × 0.1  150 × 0.05     5.95  11.8  7.5

让这9个值相加,再四舍五入取整,计算过程如下:

 6.85 + 15 + 6.25 + 14.1 + 14 + 13.1 + 5.95 + 11.8 + 7.5 = 94.55 ≈ 95

最后得到的这个结果就是高斯滤波的计算结果,滤波核的核心像素值从35改为95。
OpenCV将高斯滤波器封装成了GaussianBlur()方法,其语法如下:

 dst = cv2.GaussianBlur(src, ksize, sigmaX, sigmaY, borderType)

参数说明:

src:被处理的图像。

ksize:滤波核的大小,宽高必须是奇数,如(3, 3)、(5, 5)等。

sigmaX:卷积核水平方向的标准差。

sigmaY:卷积核垂直方向的标准差。 修改sigmaX或sigmaY的值都可以改变卷积核中的权重比例。如果不知道如何设计这2个参数值,就直接把这2个参数的值写成0,该方法就会根据滤波核的大小自动计算合适的权重比例。

borderType:可选参数,边界样式,建议使用默认值。

返回值说明:

dst:经过高斯滤波处理之后的图像。

【实例11.3】 对花朵图像进行高斯滤波操作。
分别使用大小为5×5、9×9和15×15的滤波核对花朵图像进行高斯滤波操作,水平方向和垂直方向的标准差参数值全部为0,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tbfymlO2-1639056667667)(OpenCV进阶篇.assets/image-20211122095728517.png)]

上述代码的运行结果如图11.11所示,滤波核越大,处理之后的图像就越模糊。和均值滤波、中值滤波处理的图像相比,高斯滤波处理的图像更加平滑,保留的图像信息更多,更容易辨认。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zXsixYC9-1639056667668)(OpenCV进阶篇.assets/image-20211122095747712.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qobIEi1N-1639056667668)(OpenCV进阶篇.assets/image-20211122095856218.png)]

? 图11.11 图像的高斯滤波效果

11.4 双边滤波器

不管是均值滤波、中值滤波还是高斯滤波,都会使整幅图像变得平滑,图像中的边界会变得模糊不清。双边滤波是一种在平滑处理过程中可以有效保护边界信息的滤波操作方法。
双边滤波器自动判断滤波核处于“平坦”区域还是“边缘”区域:如果滤波核处于“平坦”区域,则会使用类似高斯滤波的算法进行滤波;如果滤波核处于“边缘”区域,则加大“边缘”像素的权重,尽可能地让这些像素值保持不变。
例如,图11.12是一幅黑白拼接图像,对这幅图像进行高斯滤波,黑白交界处就会变得模糊不清,效果如图11.13所示,但如果对这幅图像进行双边滤波,黑白交界处的边界则可以很好地保留下来,效果如图11.14所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9NOGA35w-1639056667668)(OpenCV进阶篇.assets/image-20211122095927108.png)]

? 图11.12 原图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Wt7UHEi-1639056667668)(OpenCV进阶篇.assets/image-20211122095943849.png)]

? 图11.13 高斯滤波效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cERKh8Ud-1639056667669)(OpenCV进阶篇.assets/image-20211122100141070.png)]

? 图11.14 双边滤波效果
OpenCV将双边滤波器封装成bilateralFilter()方法,其语法如下:

 dst = cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace, borderType)

参数说明:

src:被处理的图像。

d:以当前像素为中心的整个滤波区域的直径。如果d<0,则自动根据sigmaSpace参数计算得到。该值与保留的边缘信息数量成正比,与方法运行效率成反比。

sigmaColor:参与计算的颜色范围,这个值是像素颜色值与周围颜色值的最大差值,只有颜色值之差小于这个值时,周围的像素才进行滤波计算。值为255时,表示所有颜色都参与计算。

sigmaSpace:坐标空间的σ(sigma)值,该值越大,参与计算的像素数量就越多。

borderType:可选参数,边界样式,建议默认。
返回值说明:

dst:经过双边滤波处理之后的图像。

【实例11.4】 对比高斯滤波和双边滤波的处理效果。

使用大小为(15, 15)的滤波核对花朵图像进行高斯滤波处理,同样使用15作为范围直径对花朵图像进行双边滤波处理,观察两种滤波处理之后的图像边缘有什么差别,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o1vvrKgX-1639056667669)(OpenCV进阶篇.assets/image-20211122100239643.png)]

上述代码的运行结果如图11.15所示,可以看出高斯滤波模糊了整个画面,但双边滤波保留了较清晰的边缘信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iL70Jm9W-1639056667669)(OpenCV进阶篇.assets/image-20211122100319332.png)]

? 图11.15 两种滤波方法效果对比

11.5 小结

噪声指的是一幅图像内部的、高亮度的像素点。图像平滑处理是指在尽量保留原图像信息的情况下,去除图像内部的这些高亮度的像素点(也就是“噪声”)。为了实现图像平滑处理,需要的工具就是滤波器。本章主要讲解了OpenCV中的4种滤波器,虽然每种滤波器的实现原理都不同,但是每种滤波器都能对图像进行图像平滑处理。读者朋友在掌握这4种滤波器的实现方法的同时,也要熟悉这4种滤波器的实现原理。

第12章 腐蚀与膨胀

腐蚀和膨胀是图像形态学中的两种核心操作,通过这两种操作可以清除或强化图像中的细节。合理使用腐蚀和膨胀,还可以实现图像开运算、闭运算、梯度运算、顶帽运算和黑帽运算等极具特点的操作。下面将对腐蚀、膨胀以及其他形态学操作进行详细的介绍。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BU9Hvg54-1639056667669)(OpenCV进阶篇.assets/image-20211125193906728.png)]

12.1 腐蚀

腐蚀操作可以让图像沿着自己的边界向内收缩。OpenCV通过“核”来实现收缩计算。“核”的英文名为kernel,在形态学中可以理解为“由n个像素组成的像素块”,像素块包含一个核心(核心通常在中央位置,也可以定义在其他位置)。像素块在图像的边缘移动,在移动过程中,核会将图像边缘那些与核重合但又没有越过核心的像素点都抹除,效果类似图12.1所示的过程,就像削土豆皮一样,将图像一层一层地“削薄”。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TcHfD008-1639056667670)(OpenCV进阶篇.assets/image-20211125193959328.png)]

? 图12.1 核腐蚀图像中的像素

OpenCV将腐蚀操作封装成erode()方法,该方法的语法如下:

 dst = cv2.erode(src, kernel, anchor, iterations, borderType, borderValue)

参数说明:

src:原始图像。

kernel:腐蚀使用的核。

anchor:可选参数,核的锚点位置。

iterations:可选参数,腐蚀操作的迭代次数,默认值为1。

borderType:可选参数,边界样式,建议默认。

borderValue:可选参数,边界值,建议默认。

返回值说明:

dst:经过腐蚀之后的图像。
图像经过腐蚀操作之后,可以抹除一些外部的细节,如图12.2所示是一个卡通小蜘蛛,如果用一个5×5的像素块作为核对小蜘蛛进行腐蚀操作,可以得到如图12.3所示的结果。小蜘蛛的腿被当成外部细节抹除了,同时小蜘蛛的眼睛变大了,因为核从内部也“削”了一圈。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GQuX4pYe-1639056667670)(OpenCV进阶篇.assets/image-20211125194100364.png)]

? 图12.2 原图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LO9YgMC8-1639056667670)(OpenCV进阶篇.assets/image-20211125194124001.png)]

? 图12.3 腐蚀之后的图像
在OpenCV做腐蚀或其他形态学操作时,通常使用numpy模块来创建核数组,例如:

 import numpy as np
 k = np.ones((5, 5), np.uint8)

这两行代码就是通过numpy模块的ones()方法创建了一个5行5列(简称5×5)、数字类型为无符号8位整数、每一个数字的值都是1的数组,这个数组作为erode()方法的核参数。除了5×5的结构,还可以使用3×3、9×9、11×11等结构,行列数越大,计算出的效果就越粗糙,行列数越小,计算出的效果就越精细。

【实例12.1】 将仙人球图像中的刺抹除。
仙人球的叶子呈针状,茎呈深绿色,如图12.4所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iwTvCycU-1639056667670)(OpenCV进阶篇.assets/image-20211125194225098.png)]

? 图12.4 仙人球
使用3×3的核对仙人球图像进行腐蚀操作,可以将图像里的刺抹除,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DnkLOVsV-1639056667671)(OpenCV进阶篇.assets/image-20211125194308740.png)]

上述代码的运行结果如图12.5所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B3Eqc3hN-1639056667671)(OpenCV进阶篇.assets/image-20211125194341145.png)]

? 图12.5 图像腐蚀操作效果

12.2 膨胀

膨胀操作与腐蚀操作相反,膨胀操作可以让图像沿着自己的边界向内扩张。同样是通过核来计算,当核在图像的边缘移动时,核会将图像边缘填补新的像素,效果类似图12.6所示的过程,就像在一面墙上反反复复地涂水泥,让墙变得越来越厚。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0jW88y9z-1639056667671)(OpenCV进阶篇.assets/image-20211125194443041.png)]

? 图12.6 核填补图像中的像素
OpenCV将膨胀操作封装成dilate()方法,该方法的语法如下:

 dst = cv2.dilate(src, kernel, anchor, iterations, borderType, borderValue)

参数说明:

src:原始图像。

kernel:膨胀使用的核。

anchor:可选参数,核的锚点位置。

iterations:可选参数,腐蚀操作的迭代次数,默认值为1。

borderType:可选参数,边界样式,建议默认。

borderValue:可选参数,边界值,建议默认。
返回值说明:

dst:经过膨胀之后的图像。
图像经过膨胀操作之后,可以放大一些外部的细节,如图12.7(a)所示的卡通小蜘蛛,如果用一个5×5的像素块作为核对小蜘蛛进行膨胀操作,可以得到如图12.7(b)所示的结果,小蜘蛛不仅腿变粗了,而且连眼睛都胖没了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Tq4mt3A-1639056667672)(OpenCV进阶篇.assets/image-20211125194535307.png)]

? 图12.7 图像膨胀操作效果

【实例12.2】 将图像加工成“近视眼”效果。
近视眼由于聚焦不准,看东西都需要放大并且模模糊糊的,利用膨胀操作可以将正常画面处理成近视眼看到的画面。采用9×9的数组作为核,对图12.8(a)进行膨胀操作。
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QnG9jkmB-1639056667672)(OpenCV进阶篇.assets/image-20211125194619331.png)]

上述代码的运行结果如图12.8所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6jRECH46-1639056667672)(OpenCV进阶篇.assets/image-20211125194637143.png)]

? 图12.8 图像膨胀操作“近视眼”效果

12.3 开运算

开运算是将图像先进行腐蚀操作,再进行膨胀操作。开运算可以用来抹除图像外部的细节(或者噪声)。
例如,图12.9是一个简单的二叉树,父子节点之间都有线连接。如果对此图像进行腐蚀操作,可以得出如图12.10所示的图像,连接线消失了,节点也比原图节点小一圈。此时再执行膨胀操作,让缩小的节点恢复到原来的大小,就得到了如图12.11所示的效果。
这3幅图就是开运算的过程,从结果中可以明显地看出:经过开运算之后,二叉树中的连接线消失了,只剩下光秃秃的节点。因为连接线被核当成“细节”抹除了,所以利用检测轮廓的方法可以统计二叉树节点数量,也就是说在某些情况下,开运算的结果还可以用来做数量统计。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YkrJfgHY-1639056667672)(OpenCV进阶篇.assets/image-20211125194659165.png)]

? 图12.9 简单的二叉树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t738eTGY-1639056667673)(OpenCV进阶篇.assets/image-20211125194754294.png)]

? 图12.10 二叉树图像腐蚀之后的效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xWsGyhUT-1639056667673)(OpenCV进阶篇.assets/image-20211125194813564.png)]

? 图12.11 对腐蚀的图像做膨胀操作

【实例12.3】 抹除黑种草图像中的针状叶子。
黑种草如图12.12(a)所示,花呈蓝色,叶子像针一样又细又长,呈羽毛状。要抹除黑种草图像中的叶子,可以使用5×5的核对图像进行开运算。
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qeCn3dVD-1639056667674)(OpenCV进阶篇.assets/image-20211125194832293.png)]

上述代码的运行结果如图12.12(b)所示,经过开运算后黑种草图像虽然略为模糊,但叶子都不见了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U09O1HA7-1639056667674)(OpenCV进阶篇.assets/image-20211125194853964.png)]

? 图12.12 图像开运算效果

12.4 闭运算

闭运算是将图像先进行膨胀操作,再进行腐蚀操作。闭运算可以抹除图像内部的细节(或者噪声)。
例如,图12.13(a)是一个身上布满斑点的小蜘蛛,这些斑点就是图像的内部细节。先将图像进行膨胀操作,小蜘蛛身上的斑点(包括眼睛)被抹除,效果如图12.13(b)所示。然后再将图像进行腐蚀操作,膨胀的小蜘蛛恢复到原来的大小,效果如图12.13(c)所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dJY1W62q-1639056667674)(OpenCV进阶篇.assets/image-20211125194952165.png)]

? 图12.13 图像闭运算效果
这3幅图就是闭运算的过程,从结果中可以明显地看出:经过闭运算后,小蜘蛛身上的花纹都被抹除了,就连眼睛也被当成“细节”抹除了。
闭运算除了会抹除图像内部的细节,还会让一些离得较近的区域合并成一块区域。

【实例12.4】 对汉字图片进行闭运算。
使用15×15的核对图12.14(a)做闭运算。因为使用的核比较大,很容易导致一些间隔较近的区域合并到一起,观察闭运算对汉字图片造成了哪些影响。
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OT67bYso-1639056667675)(OpenCV进阶篇.assets/image-20211125195012305.png)]

上述代码的运行结果如图12.14(b)所示,“田”字经过闭运算之后没有多大变化,但是“野”字经过闭运算之后,许多独立的区域因膨胀操作合并到一起,导致文字很难辨认。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IJSR1zTJ-1639056667675)(OpenCV进阶篇.assets/image-20211125195103765.png)]

? 图12.14 汉字图片闭运算效果

12.5 形态学运算

腐蚀和膨胀是形态学的基础操作,除了开运算和闭运算以外,形态学中还有几种比较有特点的运算。OpenCV提供了一个morphologyEx()形态学方法,包含所有常用的运算,其语法如下:

 dst = cv2.morphologyEx(src, op, kernel, anchor, iterations, borderType, borderValue)

参数说明:

src:原始图像。

op:操作类型,具体值如表12.1所示。

? 表12.1 形态学函数的操作类型参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c0vMUxGs-1639056667675)(OpenCV进阶篇.assets/image-20211125195140684.png)]

kernel:操作过程中使用的核。

anchor:可选参数,核的锚点位置。

iterations:可选参数,迭代次数,默认值为1。

borderType:可选参数,边界样式,建议默认。

borderValue:可选参数,边界值,建议默认。

返回值说明:

dst:操作之后得到的图像。
morphologyEx()方法实现的腐蚀、膨胀、开运算和闭运算效果与前文中介绍的效果完全一致,本节不再赘述,下面将介绍3个特点鲜明的操作:梯度运算、顶帽运算和黑帽运算。

12.5.1 梯度运算

这里的梯度是指图像梯度,可以简单地理解为像素的变化程度。如果几个连续的像素,其像素值跨度越大,则梯度值越大。
梯度运算的运算过程如图12.15所示,让原图的膨胀图减原图的腐蚀图。因为膨胀图比原图大,腐蚀图比原图小,利用腐蚀图将膨胀图掏空,就得到了原图的轮廓图。说明
梯度运算中得到的轮廓图只是一个大概轮廓,不精准。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9RG8msPp-1639056667675)(OpenCV进阶篇.assets/image-20211125195234243.png)]

? 图12.15 梯度运算过程
梯度运算的参数为cv2.MORPH_GRADIENT,下面通过一段代码实现图12.15的效果。

【实例12.5】 通过梯度运算画出小蜘蛛的轮廓。
使用5×5的核对小蜘蛛图像进行形态学梯度运算,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SSqR60Jr-1639056667676)(OpenCV进阶篇.assets/image-20211125195253240.png)]

上述代码的运行结果如图12.16所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8CM681z9-1639056667676)(OpenCV进阶篇.assets/image-20211125195322974.png)]

? 图12.16 图像梯度运算效果

12.5.2 顶帽运算

顶帽运算的运算过程如图12.17所示,让原图减原图的开运算图。因为开运算抹除图像的外部细节,“有外部细节”的图像减去“无外部细节”的图像,得到的结果就只剩外部细节了,所以经过顶帽运算之后,小蜘蛛就只剩蜘蛛腿了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RoatuBnE-1639056667676)(OpenCV进阶篇.assets/image-20211125195410644.png)]

? 图12.17 顶帽运算过程
顶帽运算的参数为cv2.MORPH_TOPHA,下面通过一段代码实现图12.18的效果。

【实例12.6】 通过顶帽运算画出小蜘蛛的腿。
使用5×5的核对小蜘蛛图像进行顶帽运算,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yaO8QMaA-1639056667676)(OpenCV进阶篇.assets/image-20211125195435250.png)]

上述代码的运算结果如图12.18所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FR02XNbh-1639056667677)(OpenCV进阶篇.assets/image-20211125195455237.png)]

? 图12.18 图像开运算效果

12.5.3 黑帽运算

黑帽运算的运算过程如图12.19所示,让原图的闭运算图减去原图。因为闭运算抹除图像的内部细节,“无内部细节”的图像减去“有内部细节”的图像,得到的结果就只剩内部细节了,所以经过黑帽运算之后,小蜘蛛就只剩下斑点、花纹和眼睛了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1t8NX17P-1639056667677)(OpenCV进阶篇.assets/image-20211125195549507.png)]

? 图12.19 黑帽运算过程
黑帽运算的参数为cv2.MORPH_BLACKHAT,下面通过一段代码实现图12.19的效果。

【实例12.7】 通过黑帽运算画出小蜘蛛身上的花纹。
使用5×5的核对小蜘蛛图像进行黑帽运算,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ltjMfK0C-1639056667677)(OpenCV进阶篇.assets/image-20211125195624673.png)]

上述代码的运行结果如图20所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xWZgHmTZ-1639056667678)(OpenCV进阶篇.assets/image-20211125195644815.png)]

? 图12.20 图像黑帽运算效果

12.6 小结

本章介绍的基础内容是腐蚀和膨胀。读者掌握了其用法,就能轻而易举地实现开运算和闭运算。其中,开运算是对图像先进行腐蚀操作,再进行膨胀操作,其作用是抹除图像外部的细节;而闭运算是对图像先进行膨胀操作,再进行腐蚀操作,其作用是抹除图像内部的细节。此外,形态学运算也是构建在腐蚀和膨胀的基础上的。其中,梯度运算是让原图的膨胀图减原图的腐蚀图,得到的结果是原图的轮廓;顶帽运算是让原图减原图的开运算图,得到的结果是图像的外部细节;黑帽运算是让原图的闭运算图减去原图,得到的结果是图像的内部细节。

第13章 图形检测

图形检测是计算机视觉的一项重要功能。通过图形检测可以分析图像中可能存在的形状,然后对这些形状进行描绘,如搜索并绘制图像的边缘,定位图像的位置,判断图像中有没有直线、圆形等。虽然图形检测涉及非常深奥的数学算法,但OpenCV已经将这些算法封装成简单的方法,开发者只要学会如何调用方法、调整参数即可很好地实现检测功能。
本章将介绍如何检测图像的形状、图像所占的区域,以及如何查找图像中出现的几何图形等。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JYC0QiOt-1639056667678)(OpenCV进阶篇.assets/image-20211125195758972.png)]

13.1 图像的轮廓

轮廓是指图像中图形或物体的外边缘线条。简单的几何图形轮廓是由平滑的线构成的,容易识别,但不规则图形的轮廓可能由许多个点构成,识别起来比较困难。
OpenCV提供的findContours()方法可以通过计算图像梯度来判断图像的边缘,然后将边缘的点封装成数组返回。findContours()方法的语法如下:

 contours, hierarchy = cv2.findContours(image, mode, methode)

参数说明:

image:被检测的图像,必须是8位单通道二值图像。如果原始图像是彩色图像,必须转为灰度图像,并经过二值化处理。

mode:轮廓的检索模式,具体值如表13.1所示。

? 表13.1 轮廓的检索模式参数值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qNkaxOlL-1639056667678)(OpenCV进阶篇.assets/image-20211125195942347.png)]

methode:

检测轮廓时使用的方法,具体值如表13.2所示。

? 表13.2 检测轮廓时使用的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FtXHcMFq-1639056667679)(OpenCV进阶篇.assets/image-20211125200003759.png)]

返回值说明:

contours:检测出的所有轮廓,list类型,每一个元素都是某个轮廓的像素坐标数组。

hierarchy:轮廓之间的层次关系。
通过findContours()方法找到图像轮廓后,为了方便开发人员观测,最好能把轮廓画出来,于是OpenCV提供了drawContours()方法用来绘制这些轮廓。drawContours()方法的语法如下:

 image = cv2.drawContours(image, contours, contourIdx, color, thickness, lineTypee, hierarchy, maxLevel, offse)

参数说明:

image:被绘制轮廓的原始图像,可以是多通道图像。

contours:findContours()方法得出的轮廓列表。

contourIdx:绘制轮廓的索引,如果为-1则绘制所有轮廓。

color:绘制颜色,使用BGR格式。

thickness:可选参数,画笔的粗细程度,如果该值为-1则绘制实心轮廓。

lineTypee:可选参数,绘制轮廓的线型。

hierarchy:可选参数,findContours()方法得出的层次关系。

maxLevel:可选参数,绘制轮廓的层次深度,最深绘制第maxLevel层。

offse:可选参数,偏移量,可以改变绘制结果的位置。

返回值说明:

image:同参数中的image,执行后原始图中就包含绘制的轮廓了,可以不使用此返回值保存结果。

【实例13.1】 绘制几何图像的轮廓。
将如图13.1所示的几何图像转换成二值灰度图像,然后通过findContours()方法找到出现的所有轮廓,再通过drawContours()方法将这些轮廓绘制成红色。轮廓的检索模式采用cv2.RETR_LIST,检测方法采用cv2.CHAIN_APPROX_NONE。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qoQuxdG7-1639056667679)(OpenCV进阶篇.assets/image-20211125200104772.png)]

? 图13.1 简单的几何图像
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GVu7lQDW-1639056667679)(OpenCV进阶篇.assets/image-20211125200222796.png)]

上述代码的运行结果如图13.2所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zu2xuce4-1639056667680)(OpenCV进阶篇.assets/image-20211125200313470.png)]

? 图13.2 绘制全部轮廓

如果使用cv2.RETR_EXTERNAL做参数则只绘制外轮廓,关键代码如下:

 contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
 cv2.drawContours(img, contours, -1, (0, 0, 255), 5)

绘制轮廓的效果如图13.3所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uJDxZ11d-1639056667680)(OpenCV进阶篇.assets/image-20211125200347818.png)]

? 图13.3 只绘制外轮廓的效果

drawContours()方法的第3个参数可以指定绘制哪个索引的轮廓。索引的顺序由轮廓的检索模式决定,例如cv2.RETR_CCOMP模式下绘制索引为0的轮廓的关键代码如下:

 contours, hierarchy = cv2.findContours(binary, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
 cv2.drawContours(img, contours, 0, (0, 0, 255), 5)

在同样的检索模式下,绘制索引为1的轮廓的关键代码如下:

 cv2.drawContours(img, contours, 1, (0, 0, 255), 5)

绘制索引为2的轮廓的关键代码如下:

 cv2.drawContours(img, contours, 2, (0, 0, 255), 5)

绘制索引为3的轮廓的关键代码如下:

 cv2.drawContours(img, contours, 3, (0, 0, 255), 5)

绘制的效果如图13.4~图13.7所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-joBl7KiJ-1639056667680)(OpenCV进阶篇.assets/image-20211125200419734.png)]

? 图13.4 绘制索引为0的轮廓

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uo0Ak2Rq-1639056667680)(OpenCV进阶篇.assets/image-20211125200519831.png)]

? 图13.5 绘制索引为1的轮廓

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Imm4zAV7-1639056667681)(OpenCV进阶篇.assets/image-20211125200538416.png)]

? 图13.6 绘制索引为2的轮廓

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LqxeG8sq-1639056667681)(OpenCV进阶篇.assets/image-20211125200557884.png)]

? 图13.7 绘制索引为3的轮廓

【实例13.2】 绘制花朵的轮廓。
为图13.8(a)所示的花朵图像绘制轮廓,首先要降低图像中的噪声干扰,进行滤波处理,然后将图像处理成二值灰度图像,并检测出轮廓,最后利用绘制轮廓的方法在原始图像中绘制轮廓。
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tZT13iZD-1639056667681)(OpenCV进阶篇.assets/image-20211125200623625.png)]

上述代码的运行结果如图13.8(b)和图13.8(c)所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6jKwXXgp-1639056667681)(OpenCV进阶篇.assets/image-20211125200704091.png)]

? 图12.8 绘制花朵轮廓效果

13.2 轮廓拟合

拟合是指将平面上的一系列点,用一条光滑的曲线连接起来。轮廓的拟合就是将凹凸不平的轮廓用平整的几何图形体现出来。本节将介绍如何按照轮廓绘制矩形包围框和圆形包围框。

13.2.1 矩形包围框

矩形包围框是指图像轮廓的最小矩形边界。OpenCV提供的boundingRect()方法可以自动计算轮廓最小矩形边界的坐标、宽和高。boundingRect()方法的语法如下:

 retval = cv2.boundingRect (array)

参数说明:

array:轮廓数组。

返回值说明:

retval:元组类型,包含4个整数值,分别是最小矩形包围框的:左上角顶点的横坐标、左上角顶点的纵坐标、矩形的宽和高。所以也可以写成x, y, w, h = cv2.boundingRect (array)的形式。

【实例13.3】 为爆炸图形绘制矩形包围框。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-33Gu2NPs-1639056667682)(OpenCV进阶篇.assets/image-20211125200758268.png)]

? 图13.9 爆炸图形
为图13.9所示的爆炸图形绘制矩形包围框,首先判断图形的轮廓,使用cv2.RETR_LIST检索所有轮廓,使用cv2.CHAIN_APPROX_SIMPLE检索图形所有的端点,然后利用cv2.boundingRect()方法计算最小矩形包围框,并通过cv2.rectangle()方法将这个矩形绘制出来,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tWLwMjpH-1639056667682)(OpenCV进阶篇.assets/image-20211125200901021.png)]

上述代码的运行结果如图13.10所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-etKSYUkz-1639056667682)(OpenCV进阶篇.assets/image-20211125200917139.png)]

? 图13.10 爆炸图形的最小矩形包围框

13.2.2 圆形包围框

圆形包围框与矩形包围框一样,是图像轮廓的最小圆形边界。OpenCV提供的minEnclosingCircle ()方法可以自动计算轮廓最小圆形边界的圆心和半径。minEnclosingCircle()方法的语法如下:

 center, radius = cv2.minEnclosingCircle(points)

参数说明:

points:轮廓数组。

返回值说明:

center:元组类型,包含2个浮点值,是最小圆形包围框圆心的横坐标和纵坐标。

radius:浮点类型,最小圆形包围框的半径。

【实例13.4】 为爆炸图形绘制圆形包围框。
为图13.9所示的爆炸图形绘制矩形包围框,首先判断图形的轮廓,使用cv2.RETR_LIST检索所有轮廓,使用cv2.CHAIN_APPROX_SIMPLE检索图形所有的端点,然后利用cv2. minEnclosingCircle()方法计算最小圆形包围框,并通过cv2.circle()方法将这个矩形绘制出来。绘制过程中要注意:圆心坐标和圆半径都是浮点数,在绘制之前要将浮点数转换成整数。
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YzrmTuZM-1639056667682)(OpenCV进阶篇.assets/image-20211125201112737.png)]

上述代码的运行结果如图13.11所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AtkeyrKl-1639056667683)(OpenCV进阶篇.assets/image-20211125201129231.png)]

? 图13.11 爆炸图形的最小圆形包围框

13.3 凸包

之前介绍了矩形包围框和圆形包围框,这2种包围框虽然已经逼近了图形的边缘,但这种包围框为了保持几何形状,与图形的真实轮廓贴合度较差。如果能找出图形最外层的端点,将这些端点连接起来,就可以围出一个包围图形的最小包围框,这种包围框叫凸包。
凸包是最逼近轮廓的多边形,凸包的每一处都是凸出来的,也就是任意3个点组成的内角均小于180°。例如,图13.12就是凸包,而图13.13就不是凸包。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mNC1DLll-1639056667683)(OpenCV进阶篇.assets/image-20211125205558522.png)]

? 图13.12 凸包

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-unNcebF6-1639056667683)(OpenCV进阶篇.assets/image-20211125205619316.png)]

? 图13.13 不是凸包
OpenCV提供的convexHull()方法可以自动找出轮廓的凸包,该方法的语法如下:

 hull = cv2.convexHull(points, clockwise, returnPoints)

参数说明:

points:轮廓数组。

clockwise:可选参数,布尔类型。当该值为True时,凸包中的点按顺时针排列,为False时按逆时针排列。

returnPoints:可选参数,布尔类型。当该值为True时返回点坐标,为False时返回点索引。默认值为True。

返回值说明:

hull:凸包的点阵数组。
下面通过一个例子演示如何绘制凸包。

【实例13.5】 为爆炸图形绘制凸包。
为图13.9所示的爆炸图形绘制凸包,首先要先判断图形的轮廓,使用cv2.RETR_LIST检索出图形的轮廓,然后使用convexHull()方法找到轮廓的凸包,最后通过polylines()方法将凸包中各点连接起来,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fXnYMX4E-1639056667683)(OpenCV进阶篇.assets/image-20211125205730374.png)]

上述代码的运行结果如图13.14所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mYOH2dGc-1639056667684)(OpenCV进阶篇.assets/image-20211125205746309.png)]

? 图13.14 爆炸图形的凸包

13.4 Canny边缘检测

Canny边缘检测算法是John F. Canny于1986年开发的一个多级边缘检测算法,该算法根据像素的梯度变化寻找图像边缘,最终可以绘制十分精细的二值边缘图像。
OpenCV将Canny边缘检测算法封装在Canny()方法中,该方法的语法如下:

 edges = cv2.Canny(image, threshold1, threshold2, apertureSize, L2gradient)

参数说明:

image:检测的原始图像。

threshold1:计算过程中使用的第一个阈值,可以是最小阈值,也可以是最大阈值,通常用来设置最小阈值。

threshold2:计算过程中使用的第二个阈值,通常用来设置最大阈值。

apertureSize:可选参数,Sobel算子的孔径大小。

L2gradient:可选参数,计算图像梯度的标识,默认值为False。值为True时采用更精准的算法进行计算。

返回值说明:

edges:计算后得出的边缘图像,是一个二值灰度图像。

在开发过程中可以通过调整最小阈值和最大阈值控制边缘检测的精细程度。当2个阈值都较小时,检测出较多的细节;当2个阈值都较大时,忽略较多的细节。

【实例13.6】 使用Canny算法检测花朵边缘。
利用Canny()方法检测图13.15(a)所示的花朵图像,分别使用10和50、100和200、400和600作为最低阈值和最高阈值检测3次,具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VlzQPJtu-1639056667684)(OpenCV进阶篇.assets/image-20211125205855069.png)]

上述代码的运行结果如图13.15所示,阈值越小,检测出的边缘越多;阈值越大,检测出的边缘越少,只能检测出一些较明显的边缘。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HhX1WPA5-1639056667684)(OpenCV进阶篇.assets/image-20211125205918990.png)]

? 图13.15 图像Canny检测效果

13.5 霍夫变换

霍夫变换是一种特征检测,通过算法识别图像的特征,从而判断图像中的特殊形状,例如直线和圆。本节将介绍如何检测图像中的直线和圆。

13.5.1 直线检测

霍夫直线变换是通过霍夫坐标系的直线与笛卡儿坐标系的点之间的映射关系来判断图像中的点是否构成直线。OpenCV将此算法封装成两个方法,分别是cv2.HoughLines()和cv2.HoughLinesP(),前者用于检测无限延长的直线,后者用于检测线段。本节仅介绍比较常用的HoughLinesP()方法。
HoughLinesP()方法名称最后有一个大写的P,该方法只能检测二值灰度图像,也就是只有两种像素值的黑白图像。该方法最后把找出的所有线段的两个端点坐标保存成一个数组。
HoughLinesP()方法的语法如下:

 lines = cv2.HoughLinesP(image, rho, theta, threshold, minLineLength, maxLineGap)

参数说明:

image:检测的原始图像。

rho:检测直线使用的半径步长,值为1时,表示检测所有可能的半径步长。

theta:搜索直线的角度,值为π/180°时,表示检测所有角度。

threshold:阈值,该值越小,检测出的直线就越多。

minLineLength:线段的最小长度,小于该长度的线段不记录到结果中。

maxLineGap:线段之间的最小距离。

返回值说明:

lines:一个数组,元素为所有检测出的线段,每条线段是一个数组,代表线段两个端点的横、纵坐标,格式为[[[x1, y1, x2, y2], [x1, y1, x2, y2]]]。注意
使用该方法前应该为原始图像进行降噪处理,否则会影响检测结果。

【实例13.7】 检测笔图像中出现的直线。

检测如图13.16所示的中性笔照片,先将图像降噪,再对图像进行边缘检测,然后利用HoughLinesP()方法找出边缘图像中的直线线段,最后用cv2.line()方法将找出的线段绘制成红色。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HFJnv0up-1639056667684)(OpenCV进阶篇.assets/image-20211125210047560.png)]

? 图13.16 笔图像
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F9KRDuUR-1639056667685)(OpenCV进阶篇.assets/image-20211126130401949.png)]

上述代码的运行结果如图13.17和图13.18所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NvCq8Qur-1639056667685)(OpenCV进阶篇.assets/image-20211126130420890.png)]

? 图13.17 笔图像的边缘检测结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EVYlpSW6-1639056667685)(OpenCV进阶篇.assets/image-20211126130515954.png)]

? 图13.18 将笔图像中检测出的线段描红

13.5.2 圆环检测

霍夫圆环变换的原理与霍夫直线变换类似。OpenCV提供的HoughCircles()方法用于检测图像中的圆环,该方法在检测过程中进行两轮筛选:第一轮筛选找出可能是圆的圆心坐标,第二轮筛选计算这些圆心坐标可能对应的半径长度。该方法最后将圆心坐标和半径封装成一个浮点型数组。
HoughCircles()方法的语法如下:

 circles = cv2.HoughCircles(image, method, dp, minDist, param1, param2, minRadius, maxRadius)

参数说明:

image:检测的原始图像。

method:检测方法,OpenCV 4.0.0及以前版本仅提供了cv2.HOUGH_GRADIENT作为唯一可用方法。

dp:累加器分辨率与原始图像分辨率之比的倒数。值为1时,累加器与原始图像具有相同的分辨率;值为2时,累加器的分辨率为原始图像的1/2。通常使用1作为参数。

minDist:圆心之间的最小距离。

param1:可选参数,Canny边缘检测使用的最大阈值。

param2:可选参数,检测圆环结果的投票数。第一轮筛选时投票数超过该值的圆环才会进入第二轮筛选。值越大,检测出的圆环越少,但越精准。

minRadius:可选参数,圆环的最小半径。

maxRadius:可选参数,圆环的最大半径。

返回值说明:

circles:一个数组,元素为所有检测出的圆环,每个圆环也是一个数组,内容为圆心的横、纵坐标和半径长度,格式为:[[[x1 ,y1, r1], [x2 ,y2, r2]]]。

注意
使用该方法前应该为原始图像进行降噪处理,否则会影响检测结果。

【实例13.8】 检测硬币图像中出现的圆环。
检测如图13.19所示的硬币照片,先将图像降噪,再将图像变成单通道灰度图像,然后利用HoughCircles()方法检测图像中可能是圆环的位置,最后通过cv2.circle()方法在这些位置上绘制圆环和对应的圆心。在绘制圆环之前,要将HoughCircles()方法返回的浮点数组元素转换成整数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vSo6G25X-1639056667685)(OpenCV进阶篇.assets/image-20211126130634358.png)]

? 图13.19 硬币图像
具体代码如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-871ibjzo-1639056667686)(OpenCV进阶篇.assets/image-20211126130715358.png)]

上述代码的运行结果如图13.20所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LLHdzygi-1639056667686)(OpenCV进阶篇.assets/image-20211126130658457.png)]

? 图13.20 检测出的圆环位置

13.6 小结

图像轮廓指的是将图像的边缘连接起来形成的一个整体,它是图像的一个重要的特征信息,通过对图像的轮廓进行操作,能够得到这幅图像的大小、位置和方向等信息,用于后续的计算。为此,OpenCV提供了findContours()方法,通过计算图像的梯度,判断图像的轮廓。为了绘制图像的轮廓,OpenCV又提供了drawContours()方法。但需要注意的是,Canny()方法虽然能够检测出图像的边缘,但这个边缘是不连续的。

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-12-10 11:04:02  更:2021-12-10 11:05:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 1:40:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码