摘要:本文介绍了基于OpenCV级联分类器的训练方法,以及相关的样本处理方法和处理代码。使用本文相关Colab笔记本和代码,可以直接在云端训练一个级联分类器。
想要直接运行文章中的Colab笔记本,戳这里。
1-训练前的准备
1.1-目录的组织
在本例中,所有的样本处理和训练都在Colab笔记本上进行,我在GoogleDrive上创建目录,储存样本。然后将其连接到Colab。
文件目录的组织:
- ./Cup/Image/GrayscaleAndResize 用于储存原始样本图片
- ./Cup/Image/Origin/Positive 用于储存原始正样本图片
- ./Cup/Image/Origin/Negative 用于储存原始负样本图片
- ./Cup/Image/GrayscaleAndResize 用于储存灰度化和调整大小后的样本图片
- ./Cup/Image/GrayscaleAndResize/Positive 用于储存灰度化和调整大小后的正样本图片
- ./Cup/Image/GrayscaleAndResize/Negative 用于储存灰度化和调整大小后的负样本图片
- ./Cup/Result 用于储存生成的中间文件及训练结果
- ./Cup/Test 用于储存用于测试分类器所用的样本
按照上面的组织结构在GoogleDrive中创建目录并上传原始数据,执行以下命令来连接到GoogleDrive(命令来自于:Jeff Heaton):
1 | try: |
你可以点击左边菜单中的文件选项,然后在打开的栏目中查看你挂载的文件目录。我的项目目录挂载在了:
/content/drive/MyDrive/ComputerVision/CascadeDetector/Cup
1.2-样本的准备
想要使用OpenCV训练级联分类器,你需要同时准备正样本和负样本。
1.2.1-正样本
正样本即(仅)包含目标物体的图片。
根据目标物体复杂度的不同,所需要的正样本数也不相同。比如说如果只想要检测单一的标志,则只需要准备这个标志。而像人脸这样复杂多变的对象,就需要大量的正样本。
我使用了二十张带把杯子的照片作为样本。
1.2.2-负样本
负样本则是要求不包含目标物体。
想要让训练出的级联分类器准确性更高,就需要选择更高质量(更有迷惑性的)的负样本。如果选取的负样本不够有区分性,则有可能导致训练出的级联分类器较浅,效果更差。(个人理解,如果我用杯子的样本进行训练,而没有在负样本中添加类似但不一样的样本的话,则这个分类器的误识别概率将会很大。)
1.3-样本的处理
1.3.1-正样本的处理
正样本的处理主要包括以下几个步骤:
- 剪裁
- 灰度化
- 大小统一化
- 生成标记文本
- 生成.vec
剪裁:
样本中的多余部分不仅会降低训练精度,还会使其效率降低。所以我们需要对样本进行剪裁,仅仅保留其主体。我将所以正样本都剪裁成了正方形样本(拍摄时已经选取了单一背景),保存在了./Cup/Image/Origin/Positive。
灰度化:
将剪裁好的样本转化为灰度图片。(这一步是必要的,否则OpenCV会报错。)你可以使用下面的程序来同时进行灰度化和大小统一化。
大小统一化:
OpenCV对正样本的大小并没有硬性规定,但是要求所用样本大小一致(且大小合适,一般来说,100x100以下是一个合适的值,较大的值可能会使训练变得很慢)。这里我选择80x80大小。你可以使用下面的程序来同时进行灰度化和大小统一化。
生成标记文本:
在完成上面的步骤之后,你需要在处理好的图片的目录中创建一个标记文本,使其包含图片的信息。下面这个程序将会自动化这个过程。
标记文本的格式为:样本路径 检测目标数 起始检测坐标X 起始检测坐标Y 图片宽W 图片高H
如果你的图片中含有多个目标,这个自动化程序可能不适用。
1 | import cv2 |
生成.vec:
然而,实际上,我们用于级联分类器训练所用到的是.vec文件。在Linux系统中,如果你安装了OpenCV,那么用于生成.vec文件的opencv_createsamples将会出现在/usr/bin下。你可以使用以下的命令来生成.vec文件。
1 | # 如果你不是在colab中执行此命令,那么请去除命令开头的"!",在接下来的命令中也是如此。 |
使用前你可以根据实际情况修改想生成pos.vec文件的路径(-vec),样本信息(-info)pos.txt的位置,以及样本所使用的统一的宽(-w)和高(-h)和生成的正样本数量(-num)。
1 | !sudo opencv_createsamples -vec /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Image/GrayscaleAndResize/Positive/pos.vec -info /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Image/GrayscaleAndResize/Positive/pos.txt -num 20 -w 80 -h 80 |
1.3.2-负样本的处理
相对于正样本的处理来说,负样本的处理极其简单。只要将负样本灰度化,并用其路径生成一个标记文件即可。使用下面的程序可以简单的做到这点。
1 | import cv2 |
在neg.txt生成后,我把pos.vec与neg.txt放在了同一个目录下,这样可以简化训练器生成的命令。(你也可以忽略这步,然后使用绝对路径)
1 | !mv /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Image/GrayscaleAndResize/Positive/pos.vec /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Result |
2-分类器生成
我们使用opencv_traincascade来对分类器进行训练:
1 | opencv_traincascade -data /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Result/cup -vec /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Result/pos.vec -bg /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Result/neg.txt -numPos 4 -numNeg 12 -numStages 4 -w 80 -h 80 |
值得注意的是,”-data”参数指定了一个用于存放训练结果的目录而不是文件名,在colab中,你应该使用绝对路径。其他参数如下:
- -numPos 每级分类器训练所使用的正样本数目,应少于正样本总数。
- -numNeg 每级分类器训练所使用的负样本数目,可大于负样本总数,但是要由可用内存决定。
- -numStages 训练分类器级数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80!opencv_traincascade -data /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Result/cup -vec /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Result/pos.vec -bg /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Result/neg.txt -numPos 15 -numNeg 100 -numStages 8 -w 80 -h 80
# output
# tcmalloc: large alloc 1879048192 bytes == 0x55bd4b6e4000 @ 0x7fe382186887 0x55bcd954e863 0x55bcd954f383 0x55bcd954b481 0x55bcd954d1e1 0x55bcd9548ae2 0x55bcd9535fff 0x7fe380641c87 0x55bcd9536c1a
# tcmalloc: large alloc 3758096384 bytes == 0x55bdbb6e4000 @ 0x7fe382186887 0x55bcd954e863 0x55bcd954f215 0x55bcd954b481 0x55bcd954d1e1 0x55bcd9548ae2 0x55bcd9535fff 0x7fe380641c87 0x55bcd9536c1a
# PARAMETERS:
# cascadeDirName: /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Result/cup
# vecFileName: /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Result/pos.vec
# bgFileName: /content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Result/neg.txt
# numPos: 15
# numNeg: 100
# numStages: 8
# precalcValBufSize[Mb] : 1024
# precalcIdxBufSize[Mb] : 1024
# acceptanceRatioBreakValue : -1
# stageType: BOOST
# featureType: HAAR
# sampleWidth: 80
# sampleHeight: 80
# boostType: GAB
# minHitRate: 0.995
# maxFalseAlarmRate: 0.5
# weightTrimRate: 0.95
# maxDepth: 1
# maxWeakCount: 100
# mode: BASIC
# Number of unique features given windowSize [80,80] : 19751440
# ===== TRAINING 0-stage =====
# <BEGIN
# POS count : consumed 15 : 15
# NEG count : acceptanceRatio 100 : 1
# tcmalloc: large alloc 2147491840 bytes == 0x55bd203b0000 @ 0x7fe3821841e7 0x7fe38137e382 0x7fe38139ee15 0x7fe38139ef21 0x55bcd953f6fb 0x55bcd953fd42 0x55bcd953fe1a 0x55bcd95491a9 0x55bcd9535fff 0x7fe380641c87 0x55bcd9536c1a
# Precalculation time: 23
# +----+---------+---------+
# | N | HR | FA |
# +----+---------+---------+
# | 1| 1| 0.02|
# +----+---------+---------+
# END>
# Training until now has taken 0 days 0 hours 3 minutes 3 seconds.
# ===== TRAINING 1-stage =====
# <BEGIN
# POS count : consumed 15 : 15
# NEG count : acceptanceRatio 100 : 0.087108
# tcmalloc: large alloc 2147491840 bytes == 0x55be9bf4a000 @ 0x7fe3821841e7 0x7fe38137e382 0x7fe38139ee15 0x7fe38139ef21 0x55bcd953f6fb 0x55bcd953fd42 0x55bcd953fe1a 0x55bcd95491a9 0x55bcd9535fff 0x7fe380641c87 0x55bcd9536c1a
# Precalculation time: 18
# +----+---------+---------+
# | N | HR | FA |
# +----+---------+---------+
# | 1| 1| 1|
# +----+---------+---------+
# | 2| 1| 0.05|
# +----+---------+---------+
# END>
# Training until now has taken 0 days 0 hours 8 minutes 6 seconds.
# ===== TRAINING 2-stage =====
# <BEGIN
# POS count : consumed 15 : 15
# NEG count : acceptanceRatio 100 : 0.0163988
# tcmalloc: large alloc 2147491840 bytes == 0x55bd203b0000 @ 0x7fe3821841e7 0x7fe38137e382 0x7fe38139ee15 0x7fe38139ef21 0x55bcd953f6fb 0x55bcd953fd42 0x55bcd953fe1a 0x55bcd95491a9 0x55bcd9535fff 0x7fe380641c87 0x55bcd9536c1a
# Precalculation time: 17
# +----+---------+---------+
# | N | HR | FA |
# +----+---------+---------+
# | 1| 1| 1|
# +----+---------+---------+
# | 2| 1| 0|
# +----+---------+---------+
# END>
# Training until now has taken 0 days 0 hours 13 minutes 8 seconds.
# ===== TRAINING 3-stage =====
# <BEGIN
# POS count : consumed 15 : 15
# NEG count : acceptanceRatio 0 : 0
# Required leaf false alarm rate achieved. Branch training terminated.
# tcmalloc: large alloc 2212167680 bytes == 0x55bd203b0000 @ 0x7fe382186887 0x55bcd954e571 0x55bcd95477f5 0x55bcd9549c55 0x55bcd9535fff 0x7fe380641c87 0x55bcd9536c1a3-分类器检测
在上文中,我们已经训练好了一个级联分类器。我们可以使用以下的代码导入一张图片并用生成的分类器进行识别,返回一个标记好的结果。
测试用样本:Photo by NordWood Themes on Unsplash输出图片:1
2
3
4
5
6
7
8
9
10
11import cv2
from google.colab.patches import cv2_imshow
cascade = cv2.CascadeClassifier('/content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Result/cup/cascade.xml')
img = cv2.imread('/content/drive/MyDrive/ComputerVision/CascadeDetector/Cup/Test/1.jpg')
img_gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
detector = cascade.detectMultiScale(img_gray)
for (x,y,w,h) in detector:
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),1)
cv2_imshow(img)
可以看到,这个分类器可以简单的识别样本,但是精度不忍直视。(它好像抓到了杯把的特征😂)在下一篇文章中,我将更深入的找出训练一个高可用性的级联分类器的方法。