0.建立训练目录
文件夹: train/
1.建立正负样本
新建3个文件夹:train/pos/、train/neg/、train/xml/
pos文件夹:
放置正样本,尺寸要一致:如
20
∗
20
20*20
20∗20(一般用于Haar特征),
24
∗
24
24*24
24∗24(LBP特征)
neg文件夹:
放置负样本,正样本的尺寸要保证不大于负样本的尺寸
xml文件夹:
级联分类器xml文件的输出目录
2.生成正负样本的txt文件
生成正样本txt文件 train/pos/pos.txt:
内容:图片名 类别编号 左上角x 左上角y 右下角x 右下角y
pos_image1.png 1 0 0 30 30
pos_image2.png 1 0 0 30 30
… …
生成负样本txt文件 train/neg/neg.txt:
内容:图片路径名
neg/neg_image1.png
neg/neg_image2.png
… …
import os
def _get_directory_files(path, fileType, filePaths):
if not os.path.exists(path):
return
files = os.listdir(path)
for f in files:
npath = path + '/' + f
if (os.path.isfile(npath)):
if (os.path.splitext(npath)[1] == fileType):
filePaths.append(f)
if (os.path.isdir(npath)):
if (f[0] == '.'):
pass
else:
_get_directory_files(npath, fileType, filePaths)
return
def _write_txt(txt_path,img_dir,is_pos,img_size,neg_dir='neg'):
imgpaths=[]
_get_directory_files(img_dir,'.png',imgpaths)
with open(txt_path, "a") as f:
for img_p in imgpaths:
if is_pos:
f.write('%s 1 0 0 %d %d\n' % (img_p,img_size[0],img_size[1]))
else:
f.write('%s/%s\n' % (neg_dir, img_p))
return
if __name__ == "__main__":
txt_pos = './train/pos/pos.txt'
txt_neg = './train/neg/neg.txt'
pos_img_dir = './train/pos'
neg_img_dir = './train/neg'
_write_txt(txt_pos,pos_img_dir,True,(30,30))
_write_txt(txt_neg,neg_img_dir,False,(30,30))
3.生成 pos.vec描述文件
在 train/ 目录下,运行命令:
opencv_createsamples -vec pos.vec -info pos/pos.txt -bg neg/neg.txt -num 10000 -w 30 -h 30
命令参数如下:
-
info 输入正样本描述文件,默认NULL
-
img 输入图像文件名,默认NULL
-
bg 负样本描述文件,文件中包含一系列的被随机选作物体背景的图像文件名,默认NULL
-
num 生成正样本的数目,默认1000
-
bgcolor 背景颜色,表示透明颜色,默认0
-
bgthresh 颜色容差,所有处于bgcolor-bgthresh和bgcolor+bgthresh之间的像素被置为透明像素,也就是将白噪声加到前景图像上,默认80
-
inv 前景图像颜色翻转标志,如果指定颜色翻转,默认0(不翻转)
-
randinv 如果指定颜色将随机翻转,默认0
-
maxidev 前景图像中像素的亮度梯度最大值,默认40
-
maxxangle X轴最大旋转角度,以弧度为单位,默认1.1
-
maxyangle Y轴最大旋转角度,以弧度为单位,默认1.1
-
maxzangle Z轴最大旋转角度,以弧度为单位,默认0.5
输入图像沿着三个轴进行旋转,旋转角度由上述3个值限定。 -
show 如果指定,每个样本都将被显示,按下Esc键,程序将继续创建样本而不在显示,默认为0(不显示)
-
scale 显示图像的缩放比例,默认4.0
-
w 输出样本宽度,默认24
-
h 输出样本高度,默认24
-
vec 输出用于训练的.vec文件,默认NULL
4.训练cascade分类器
在 train/ 目录下,运行命令:
opencv_traincascade -data xml -vec pos.vec -bg neg/neg.txt -numPos 8000 -numNeg 16000 -numStages 20 -featureType LBP -w 30 -h 30
命令参数如下:
-
data 目录名xml,存放训练好的分类器,如果不存在训练程序自行创建
-
vec pos.vec文件,由opencv_createsamples生成
-
bg 负样本描述文件, neg/neg.txt
-
numPos 每级分类器训练时所用到的正样本数目。
应当注意,这个数值一定要比正样本时的总数少,不然会报can not get new positive sample.理由:minHitRate:影响每个强分类器阈值,当设置为0.95时如果正训练样本个数为10000个,那么其中的500个就很可能背叛别为负样本,第二次选择的时候必须多选择后面的500个,按照这种规律我们为后面的每级多增加numPos*minHitRate个正样本,根据训练的级数可以得到如下公式:
n u m P o s + ( n u m S t a g e s − 1 ) ∗ n u m P o s ∗ ( 1 − m i n H i t R a t e ) < = 准 备 的 训 练 样 本 numPos+(numStages-1)*numPos*(1-minHitRate)<=准备的训练样本 numPos+(numStages−1)∗numPos∗(1−minHitRate)<=准备的训练样本 -
numNeg 每级分类器训练时所用到的负样本数目,可以大于-bg指定的图片数目
-
numStages 训练分类器的级数,默认20级,一般在14-25层之间均可。
如果层数过多,分类器的fals alarm就更小,但是产生级联分类器的时间更长,分类器的hitrate就更小,检测速度就慢。如果正负样本较少,层数没必要设置很多。 -
precalcValBufSize 缓存大小,用于存储预先计算的特征值,单位MB
-
precalcIdxBufSize 缓存大小,用于存储预先计算的特征索引,单位MB
-
baseFormatSave 仅在使用Haar特征时有效,如果指定,级联分类器将以老格式存储
-
stageType 级联类型,{ CC_BOOST }
-
featureType 特征类型,目前只支持LBP、HOG、Haar三种特征。但是HAAR训练非常非常的慢,而LBP则相对快很多,因为HAAR需要浮点运算,精度自然比LBP更高,但是LBP的效果也基本能达到HAAR的效果,推荐使用LBP。
-
w,h 训练样本的尺寸,必须跟使用opencv_createsamples创建的训练样本尺寸保持一致,并且-w和-h的比例必须符合真实目标的比例.
-
bt Boosted分类器类型,{DAB-discrete Adaboost, RAB-RealAdaboost, LB-LogiBoost, GAB-Gentle Adaboost}
-
minHitRate 分类器的每一级希望得到的最小检测率,总的最大检测率大约为min_hit_rate^number_of_stages
-
maxFalseAlarmRate 分类器的每一级希望得到的最大误检率,总的误检率大约为max_false_rate^number_of_stages
-
weightTrimRate Specifies whether trimming should beused and its weight. 一个还不错的数值是0.95
-
maxDepth 弱分类器的最大深度,一个不错数值是1,二叉树
-
maxWeightCount 每一级中弱分类器的最大数目
-
mode 训练过程使用的Haar特征类型,有BASIC/CORE/ALL三种特征组合待选的,默认情况为BASIC,三种情况下对应的特征选取分别如下:
5.目标检测
detectMultiScale()函数参数:
cv2.CascadeClassifier.detectMultiScale(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]]) → objects
cv2.CascadeClassifier.detectMultiScale(image, rejectLevels, levelWeights[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize[, outputRejectLevels]]]]]]) → objects
参数如下:
-
image: Matrix of the type CV_8U containing an image where objects are detected. 灰度图
-
objects:Vector of rectangles where each rectangle contains the detected object.
-
scaleFactor:Parameter specifying how much the image size is reduced at each image scale. 图像尺度参数,默认1.1
-
minNeighbors:Parameter specifying how many neighbors each candidate rectangle should have to retain it. 为每一个级联矩形应该保留的临近个数,默认为3,即至少有3次检测到目标,才认为是目标。
-
flags:Parameter with the same meaning for an old cascade as in the function cvHaarDetectObjects. It is not used for a new cascade.
CV_HAAR_DO_CANNY_PRUNING,利用边缘检测来排除一些边缘很少或者很多的图像区域
CV_HAAR_SCALE_IMAGE,按正常比例检测
CV_HAAR_FIND_GIGGEST_OBJECT,只检测最大的物体
CV_HAAR_DO_ROUGH_SEARCH,只做粗略检测,默认值为0 -
minSize – Minimum possible object size. Objects smaller than that are ignored.
-
maxSize – Maximum possible object size. Objects larger than that are ignored.
import cv2, time
import numpy as np
import os.path
def get_hw_by_short_size(im_height, im_width, resize):
short_size, max_size = resize
im_size_min = np.min([im_height, im_width])
im_size_max = np.max([im_height, im_width])
scale = (short_size + 0.0) / im_size_min
if scale * im_size_max > max_size:
scale = (max_size + 0.0) / im_size_max
resized_height, resized_width = int(round(im_height * scale)), int(
round(im_width * scale))
return resized_height, resized_width
class car_detector:
def __init__(self, cascade_file, max_detect_hw=(400, 600)):
if not os.path.isfile(cascade_file):
raise RuntimeError("%s: not found" % cascade_file)
self._cascade = cv2.CascadeClassifier(cascade_file)
self._max_detect_hw = max_detect_hw
def detect_image(self, image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.equalizeHist(gray)
cars = self._cascade.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=15, minSize=(60, 60))
for (x, y, w, h) in cars:
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)
return image
def detect_video(self, video_path, start_frame, end_frame, ):
cap = cv2.VideoCapture(video_path)
cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
org_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
org_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
h, w = get_hw_by_short_size(org_h, org_w, self._max_detect_hw)
while (start_frame < end_frame):
start_frame += 1
ret, image = cap.read()
if not ret: return
resized_img = cv2.resize(image, (w, h), interpolation=cv2.INTER_CUBIC)
result = self.detect_image(resized_img)
cv2.imshow("Detect", result)
cv2.waitKey(1)
if __name__ == "__main__":
car_cascade_lbp_21 = './train/xml/cascade.xml'
video_path = "./test.mp4"
start_frame = 0
end_frame = 300
detect = car_detector(car_cascade_lbp_21)
detect.detect_video(video_path, start_frame, end_frame)
总结
车辆检测,在训练阶段:
HOG特征:
正样本尺寸
30
∗
30
30*30
30∗30,训练速度非常快,结果不收敛。
正样本尺寸
64
∗
64
64*64
64∗64,训练速度较快,结构收敛。
但是, OpenCV 3.x 中, CascadeClassifier方法不支持 HOG特征。
HAAR特征:
正样本尺寸
20
∗
20
20*20
20∗20,训练速度非常慢,结果不收敛。
LBP特征:
正样本尺寸
30
∗
30
30*30
30∗30,训练速度较快,结果收敛。