1.收集正样本
这里需要注意的是,正样本图需要裁剪,使目标物体轮廓很清晰,且正样本图越多越好。
2.处理正样本
将正样本图片转为灰度图,方便后续处理。
def convert_gray(f, **args): # 图片处理与格式化的函数
rgb = io.imread(f) # 读取图片
gray = color.rgb2gray(rgb) # 将彩色图片转换为灰度图片
dst = transform.resize(gray, (40, 40)) # 调整大小,图像分辨率为40*40
return dst
if __name__ == '__main__':
'''
批量转灰度图
'''
datapath = r'C:\User\PycharmProjects\cascadeProject\redball' # 图片所在的路径
str = datapath + '/*.jpg' # 识别.jpg的图像
coll = io.ImageCollection(str, load_func=convert_gray) # 批处理
for i in range(len(coll)):
io.imsave('C:/Users/15074749770/PycharmProjects/cascadeProject/pos/' + np.str(i) + '.jpg', coll[i]) # 保存图片
3.收集负样本
关于负样本,只要不含有正样本图片即可,最好是识别场景的图片。
我找到一个负样本下载链接。点击下载,下载后如图。
4.生成描述文件
正负样本描述性文件生成
import os
def create_pos_n_neg():
for file_type in ['neg']: #此处修改neg或pos即可
for img in os.listdir(file_type):
if (file_type == 'neg'):
line = file_type + '/' + img + '\n'
with open('bg.txt', 'a') as f:
f.write(line)
elif (file_type == 'pos'):
line = file_type + '/' + img + ' 1 0 0 50 50\n'
with open('info.txt', 'a') as f:
f.write(line)
if __name__ == '__main__':
create_pos_n_neg()
正样本描述文件 info.txt
负样本描述文件 bg.txt
5.训练分类器
首先准备工具,把opencv自带的分类器也放在同一个目录下
生成pos.vec文。在当前目录打开控制台程序,输入
opencv_createsamples.exe -vec info.vec -info info.txt -bg bg.txt -num 50 -w 50 -h 50
其中,-info字段填写正样本描述文件;-vec用于保存制作的正样本;-num制定正样本的数目;-w和-h分别指定正样本的宽和高。
训练分类器
opencv_traincascade.exe -data data -vec info.vec -bg bg.txt -numPos 5 -numNeg 400 -numStages 10 -w 50 -h 50 -minHitRate 0.999 -maxFalseAlarmRate 0.2 -weightTrimRate 0.95 -featureType LBP
字段说明如下:
data data:训练后data目录下会存储训练过程中生成的文件
vec pos.vec:Pos.vec是通过opencv_createsamples生成的vec文件
bg bg.txt:bg.txt是负样本文件的数据
numPos :正样本的数目,这个数值一定要比准备正样本时的数目少,不然会报can not get new positive sample.
numNeg :负样本数目,数值可以比负样本大
numStages :训练分类器的级数
w 50:必须与opencv_createsample中使用的-w值一致
h 50:必须与opencv_createsample中使用的-h值一致
注:-w和-h的大小对训练时间的影响非常大,我测试了两个不同尺寸下的训练,分别是Size(50,50)和Size(70,70),后者所用的时间至少是前者的4-5倍。网上有博客说-w和-h的比例必须符合真实目标的比例。
minHitRate 0.9999:分类器的每一级希望得到的最小检测率,总的最大检测率大约为min_hit_ratenumber_of_stages
minHitRate:影响每个强分类器阈值,当设置为0.95时如果正训练样本个数为10000个,那么其中的500个就很可能背叛别为负样本,第二次选择的时候必须多选择后面的500个,按照这种规律为后面的每级多增加
numPosminHitRate个正样本,根据训练的级数可以得到如下公式
numPos+(numStages-1)numPos(1-minHitRate)<=准备的训练样本
featureType LBP: 训练时,提取图像特征的类型,目前只支持LBP、HOG、Haar三种特征。但是HAAR训练非常非常的慢,而LBP则相对快很多,因为HAAR需要浮点运算,精度自然比LBP更高,但是LBP的效果也基本能达到HAAR的效果,所以我选择使用LBP。
maxFalseAlarmRate 0.2:分类器的每一级希望得到的最大误检率,总的误检率大约为max_false_rate*number_of_stages
mode ALL:选择用来训练的haar特征集的种类。basic仅仅使用垂直特征。all使用垂直和45度角旋转特征。
最后,使用分类器进行检测
import numpy as np
from skimage import io, transform, color
import os
import cv2
def convert_gray(f, **args): # 图片处理与格式化的函数
rgb = io.imread(f) # 读取图片
gray = color.rgb2gray(rgb) # 将彩色图片转换为灰度图片
dst = transform.resize(gray, (40, 40)) # 调整大小,图像分辨率为40*40
return dst
def create_pos_n_neg():
for file_type in ['pos']:
for img in os.listdir(file_type):
if (file_type == 'pos'):
line = file_type + '/' + img + ' 1 0 0 50 50\n'
with open('info.txt', 'a') as f:
f.write(line)
print "pos"
elif (file_type == 'neg'):
line = file_type + '/' + img + '\n'
with open('bg.txt', 'a') as f:
f.write(line)
print "neg"
def image_detect(imgpath):
cv2.namedWindow("image detect",cv2.WINDOW_NORMAL)
image = cv2.imread(imgpath)
# 告诉OpenCV使用红球识别分类器
classfier = cv2.CascadeClassifier(r"C:\Users\15074749770\PycharmProjects\cascadeProject\data\cascade.xml")
# 识别出红球后要画的边框的颜色,RGB格式
color = (0, 255, 0)
grey = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# scaleFactor搜索前后两次窗口大小比例系数,默认1.2,即每次搜索窗口扩大20%
# minNeighbors构成检测目标的相邻矩形的最小个数如果组成检测目标的小矩形的个数和小于minneighbors - 1都会被排除,如果minneighbors为0则函数不做任何操作就返回所有被检候选矩形框
# 能检测的最小尺寸和最大尺寸
ballRects = classfier.detectMultiScale(grey, scaleFactor=1.2, minNeighbors=15, minSize=(5, 5))
if len(ballRects) > 0: # 大于0则检测到
for ballRect in ballRects: # 单独框出每一张
x, y, w, h = ballRect
cv2.rectangle(image, (x, y), (x + w , y + h), color, 2)
# 显示图像
cv2.imshow("image detect", image)
c = cv2.waitKey(0)
# 释放摄像头并销毁所有窗口
cv2.destroyAllWindows()
# 摄像头人脸检测
def face_image(src):
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
face_detector = cv2.CascadeClassifier(r"C:\Users\15074749770\PycharmProjects\cascadeProject\opencv-3.3.1\data\haarcascades\haarcascade_eye.xml")
faces = face_detector.detectMultiScale(gray, 1.2, 40) # 第二个参数是移动距离,第三个参数是识别度,越大识别度越高
for x, y, w, h in faces:
cv2.rectangle(src, (x, y), (x + w, y + h), (0, 0, 255), 2) # 后两个参数,一个是颜色,一个是边框宽度
cv2.imshow("result", src)
if __name__ == '__main__':
'''
批量转灰度图
'''
datapath = r'C:\Users\15074749770\PycharmProjects\cascadeProject\redball' # 图片所在的路径
str = datapath + '/*.jpg' # 识别.jpg的图像
coll = io.ImageCollection(str, load_func=convert_gray) # 批处理
for i in range(len(coll)):
io.imsave('C:/Users/15074749770/PycharmProjects/cascadeProject/pos/' + np.str(i) + '.jpg', coll[i]) # 保存图片
'''
正负样本生成描述性文件
'''
# create_pos_n_neg()
'''
调用分类器检测图片
'''
# image_detect("redball/2.jpg")
'''
摄像头人脸检测
'''
capture = cv2.VideoCapture(0)
while (True):
ret, frame = capture.read()
frame = cv2.flip(frame, 1)
face_image(frame)
if cv2.waitKey(10) & 0xFF == ord('q'): # 键盘输入q退出窗口,不按q点击关闭会一直关不掉 也可以设置成其他键。
break
face_image()
cv2.waitKey(0)
cv2.destroyAllWindows()