利用opencv训练基于Haar特征、LBP特征、Hog特征的分类器cascade.xml

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/lql0716/article/details/72566839

利用opencv训练基于Haar特征、LBP特征、Hog特征的分类器cascade.xml


author@jason_ql
http://blog.csdn.net/lql0716


1 利用opencv训练基于Haar特征、LBP特征、Hog特征的分类器cascade.xml

  • 该训练是基于adaboost算法训练的。

  • 工具:
    1、opencv_createsamples.exe
    2、opencv_traincascade.exe

    这两个exe执行程序的路径为:

    C:\opencv\build\x64\vc12\bin

    C:\opencv\build\x86\vc12\bin

    在opencv安装目录下查找即可找到

1.1 训练步骤

  • 1、搜集正样本、负样本

    正样本:只含有目标的局部图(若是全图,则需要把目标截取出来,比如训练人脸,则把人脸从含有人脸的图片中截取出来,尺寸要一致),且背景不要太过复杂,灰度图

    正样本大小:20*20(一般用于Haar特征),24*24(LBP特征)

    正样本数量:一般大于等于2000

    负样本:不含目标的任何图片,灰度图

    负样本大小:60*60

    负样本数量:一般大于等于5000

    :正样本尺寸越小,训练的时间越短,但正样本的尺寸要保证小于负样本的尺寸

  • 2、在路径C:\opencv\build\x64\vc12\bin下新建3个文件夹:pos、neg、xml

    pos文件夹:存放正样本

    neg文件夹:存放负样本

    xml文件夹:存放训练的xml格式文件

    此处输入图片的描述

    :若使用的是路径C:\opencv\build\x86\vc12\bin,可能会提示错误“Training parameters are loaded from the parameter file in data folder! Please empty the data folder if you want to use your own set of parameters”,若不提示错误,使用x86路径或x64路径均可。

  • 3、将正样本、负样本分别复制到pos文件夹、neg文件夹

  • 4、cmd打开dos命令窗口,进入pos路径下

    • 输入以下命令,生成pos.txt
      dir/b >pos.txt

    此处输入图片的描述

    • 生成pos.txt后,将png替换为:
      png 1 0 0 24 24
    • 删除最后一行的pos.txt

    此处输入图片的描述

    此处输入图片的描述

  • 5、同样进入neg路径下

    • 输入以下命令,生成neg.txt
      dir/b >neg.txt

    此处输入图片的描述

    • 生成neg.txt后,将neg.txt中的图片名字前面加入路径:
      neg/
    • 删除最后一行的neg.txt

    此处输入图片的描述

  • 6、进入路径C:\opencv\build\x64\vc12\bin,即pos、neg、xml所在的路径,输入以下命令,生成pos.vec

opencv_createsamples.exe -info pos\pos.txt -vec pos.vec -bg neg\neg.txt -num 2000 -w 24 -h 24
  • 7、输入以下命令开始训练
opencv_traincascade.exe -data xml -vec pos.vec -bg neg\neg.txt 
-numPos 1800 -numNeg 4000 -numStages 20 -featureType LBP -w 24 -h 24

-numStages的参数一般默认为20,最好大于等于20,级数太小的话,训练的效果较差

  • 1、错误提示:
    “Traincascade Error:Bad argument(Can not get new positive sample.The most possible reason is insufficient count of samples in given vec-file.”
    出现这个提示,可能是正样本参数太接近总的正样本数导致。
  • 2、错误提示:
    “Required leaf false alarm rate achieved. Branch training terminated – it’s impossible to build classifier with good false alarm on this negative images. Check your negative images are really negative”
    出现这个提示,可能是因为负样本中有类似正样本的图片,把这些图片筛掉
  • 8、训练结束后,xml文件夹下的cascade.xml文件就是训练好的分类器

1.2 批量调整样本图片尺寸的方法

使用python可以很方便的批量调整图片的尺寸(也可以用C++,但是代码写起来比较麻烦),方法如下。

  • 1、视频逐帧读取为图片
    逐帧读取图片,并按从1开始对图片进行编号
import numpy as np
import cv2
cap = cv2.VideoCapture('C:/Users/vid.mp4')  #读取视频
c = 1
while (cap.isOpened()):
    ret, frame = cap.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    cv2.imshow('frame', gray)
    cv2.imwrite('C:/Users/photo/' + '%s'%c + '.jpg', frame)  #图片写入相应的路径
    c = c + 1
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()
  • 2、批量调整图片尺寸
c = 1
for i in range(1, 2000):
    img = cv2.imread('D:/test/' + str(i) + '.jpg') #按序号读取图片
    if img==None:  #如果图片不存在,则跳过本次循环
        continue
    dst1 = img[:120, :120] #截取120*120的图片
    dst2 = cv2.resize(dst1, (60,60), 0, 0, cv2.INTER_LINEAR)   #调整图片大小,即缩放,其中60为图片尺寸
    dst3 = cv2.resize(img, (60,60), 0, 0, cv2.INTER_LINEAR)  #该操作是直接对原图进行缩放,这样会使得图片变形,故最好先截取为等宽等长的图片,再进行缩放
    cv2.imwrite('D:/test/photo/'+str(c)+'.png', dst2)
    c = c + 1
print 'its over'

1.3 训练步骤中的参数详解

无论是Haar特征训练,还是LBP特征训练或Hog特征训练,其图片尺寸都不要太大

一般,训练Haar所需的正样本图片为:20*20,训练LBP所需的正样本图片为:24*24,训练HOG所需的正样本图片为:60*60,负样本图片要大于等于正样本图片

  • 1、生成pos.vec的命令:
opencv_createsamples.exe -info pos\pos.txt -vec pos.vec -bg neg\neg.txt -num 2000 -w 24 -h 24
  • 2、生成pos.vec的参数:

    -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

  • 3、训练命令:

opencv_traincascade.exe -data xml -vec pos.vec -bg neg\neg.txt 
-numPos 1800 -numNeg 4000 -numStages 20 -featureType LBP -w 24 -h 24
  • 4、训练参数:

-data 目录名xml,存放训练好的分类器,如果不存在训练程序自行创建

-vec pos.vec文件,由opencv_createsamples生成

-bg 负样本描述文件, neg\neg.txt

-numPos 每级分类器训练时所用到的正样本数目

-numNeg 每级分类器训练时所用到的负样本数目,可以大于-bg指定的图片数目

-numStages 训练分类器的级数,默认20级,一般在14-25层之间均可。
如果层数过多,分类器的fals alarm就更小,但是产生级联分类器的时间更长,分类器的hitrate就更小,检测速度就慢。如果正负样本较少,层数没必要设置很多。

-precalcValBufSize 缓存大小,用于存储预先计算的特征值,单位MB

-precalcIdxBufSize 缓存大小,用于存储预先计算的特征索引,单位M币

-baseFormatSave 仅在使用Haar特征时有效,如果指定,级联分类器将以老格式存储

  • 5、级联参数cascadeParams:

    -stageType 级联类型,staticconst char* stageTypes[] = { CC_BOOST };

    -featureType 特征类型,staticconst char* featureTypes[] = { CC_HAAR, CC_LBP, CC_HOG };

    -w
    -h 训练样本的尺寸,必须跟使用opencv_createsamples创建的训练样本尺寸保持一致

  • 6、Boosted分类器参数stageParams:

    -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 每一级中弱分类器的最大数目

  • 7、Haar特征参数featureParams

    -mode 训练过程使用的Haar特征类型,CORE-Allupright ALL-All Features BASIC-Viola

1.4 测试分类器的效果

以下测试效果为opencv自带的人脸分类器测试效果代码。对于自己训练的分类器,只需将分类器文件及路径在以下代码中替换一下就可以了。

1.4.1 视频中识别目标

  • 1、C++代码
#include <opencv2\objdetect\objdetect.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(){

    cv::Mat img, gray;
    std::vector<Rect> face;
    CascadeClassifier face_cascade;
    if (!face_cascade.load("C:/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml")){ printf("--(!)Error loading face\n"); return -1; };

    cv::VideoCapture cap(0);
    if (!cap.isOpened()){
        return -1;
    }

    cv::namedWindow("Video", 1);
    while (1){

        char key = cv::waitKey(1);
        if (key == 'q'){
            cv::destroyWindow("Video");
            break;
        }

        cap >> img;
        cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);

        face_cascade.detectMultiScale(gray, face, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(30, 30));
        for (size_t i = 0; i < face.size(); i++){
            cv::Point center(face[i].x + face[i].width*0.5, face[i].y + face[i].height*0.5);
            cv::ellipse(img, center, Size(face[i].width*0.5, face[i].height*0.5), 0, 0, 360, Scalar(255, 0, 0), 4, 8, 0);
        }

        cv::imshow("Video", img);
    }

    cap.release();
    return 0;
}
  • 2、python代码
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 21 17:37:53 2017

@author: User
"""

##******************************************************************************
##                       利用opencv的分类器,检测视频中的人脸
##******************************************************************************
import cv2

face_haar = cv2.CascadeClassifier("C:/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml")  #读取人脸分类器,可在opencv的相应路径下找到

path = 'D:/test/test.avi'  #视频

cam = cv2.VideoCapture(0)  #读取摄像头
if cam==False:  #如果未发现摄像头,则读取视频
    cam = cv2.VideoCapture(path)

while True:

    _, img = cam.read()
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    faces = face_haar.detectMultiScale(gray_img, 1.3, 5)  #检测人脸
    for face_x,face_y,face_w,face_h in faces:
        cv2.rectangle(img, (face_x, face_y), (face_x+face_w, face_y+face_h), (0,255,0), 2)

    cv2.imshow('img', img)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        cv2.destroyAllWindows()
        break

cam.release()
cv2.waitKey(0)
cv2.destroyAllWindows()

1.4.2 图片中识别目标

  • 1、C++代码
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(){

    cv::Mat img, gray;
    std::vector<Rect> face;
    CascadeClassifier face_cascade;
    if (!face_cascade.load("C:/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml")){
        printf("--(!)Error loading face\n");
        return -1;
    };

    img = cv::imread("d:/photo/08.jpg");
    cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
    //cv::GaussianBlur(gray, gray, Size(5, 5), 0, 0, 4);  //高斯滤波
    face_cascade.detectMultiScale(gray, face, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(30, 30));
    for (size_t i = 0; i < face.size(); i++){
        cv::Point center(face[i].x + face[i].width*0.5, face[i].y + face[i].height*0.5);
        cv::ellipse(img, center, Size(face[i].width*0.5, face[i].height*0.5), 0, 0, 360, Scalar(255, 0, 0), 4, 8, 0);
    }

    cv::imwrite("d:/photo/08_1.jpg", img);
    cv::namedWindow("img", cv::WINDOW_NORMAL);
    cv::imshow("img", img);
    cv::waitKey(0);

    return 0;
}

原图:
此处输入图片的描述

效果图:
此处输入图片的描述

效果图中出现了误识别,所以opencv所带的分类器准确率并不是很高,但也可以通过调整函数detectMultiScale()的参数来提高识别率,或者检测之前进行高斯滤波。

  • 2、python代码
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 21 17:37:53 2017

@author: User
"""

#******************************************************************************
#                       利用opencv的分类器,检测单张图片的人脸
#******************************************************************************
import cv2

img = cv2.imread('D:/photo/08.jpg')

# 加载分类器
face_haar = cv2.CascadeClassifier("C:/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml")

# 把图像转为黑白图像
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#gray_img = cv2.GaussianBlur(gray_img, (5,5), 0, 0) #高斯滤波
# 检测图像中的所有脸
faces = face_haar.detectMultiScale(gray_img, 1.1, 4)
for face_x,face_y,face_w,face_h in faces:
    cv2.rectangle(img, (face_x, face_y), (face_x+face_w, face_y+face_h), (0,255,0), 2)

#cv2.imwrite('d:/photo/08_3.jpg', img)
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.imshow('img', img)
cv2.waitKey(0) 
cv2.destroyAllWindows()

效果图:
此处输入图片的描述

先高斯滤波再检测人脸的效果图:
此处输入图片的描述

1.4.3 detectMultiScale()函数参数

Detects objects of different sizes in the input image. The detected objects are returned as a list of rectangles.

C++: void CascadeClassifier::detectMultiScale(const Mat& image, vector<Rect>& objects, double scaleFactor=1.1, int minNeighbors=3, int flags=0, Size minSize=Size(), Size maxSize=Size())

Python: cv2.CascadeClassifier.detectMultiScale(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]]) → objects

Python: cv2.CascadeClassifier.detectMultiScale(image, rejectLevels, levelWeights[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize[, outputRejectLevels]]]]]]) → objects
  • Parameters:

    • cascade – Haar classifier cascade (OpenCV 1.x API only). It can be loaded from XML or YAML file using Load(). When the cascade is not needed anymore, release it using cvReleaseHaarClassifierCascade(&cascade). 传入生成的cascade.xml分类器文件

    • 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.

参考资料

展开阅读全文

没有更多推荐了,返回首页