最近小学期的课程中,导师要求做了一个在图像中识别特定人物的任务(识别特朗普)。我最开始使用反投影直方图,但是效果极其渣,后来选用神经网络的方法做分类进行识别,但网上写的都不甚详细,于是打算自己写一个作为记录。
OpenCV3中使用的分类器是基于级联神经网络,其可以通过级数的增加提高分类的正确率。
步骤一:
首先,我们需要知道训练一个分类器我们要准备些什么:
A.我们先建立一个空白的文件夹
B.我们在这个空白的文件夹上建立一个名为posdata的文件夹、一个名为negdata的文件夹、一个名为xml的文件夹,这些文件夹现在为止都是空白的,我们先什么都不需要去添加进去。
C.将下列文件目录拷贝到我们刚刚建立的那个文件夹的主目录下,就像这样
D.在这之后,我们就需要收集我们的数据了。我们将特朗普的面颊正样本放在名为posdata的文件夹中,将负样本放在名为negdata的文件夹中,正负样本的链接在这里。
正样本
负样本
E.在放好之后,我们需要做出有关正负样本的标签文件。我们首先打开 posdata 文件夹做正样本的标签文件。我们在 posdata中新建一个 txt 文档,在其中打上这一段代码
dir /b/s/p/w *.jpg > posdata.txt
之后,我们将其保存为 .bat 格式,就像这样:
这时在文档中就像这样:
我们运行 neg.bat 就会生成一个 posdata.txt,我们使用 Ctrl+h 更换这个文档中的内容,将绝对路径改为相对路径,更改前和更改后的状态就像这样:
在这时,我们对这个文件继续进行更改,将 jpg 更换为
jpg 1 0 0 20 20
更改之后的文档就像这样:
之后,我们把这个posdata的文档移动到 training 文件夹的根目录下(剪切)
F.对正样本处理好之后,我们继续对negdata进行处理,所有处理步骤和上述一样,但是我们不需要做 jpg 更改那一个步骤,更改好的文档就像这样,之后,我们也把他移动到主目录下。
G.在这之后,我们打开 cmd 将 cmd 的处理目录设置到我们现在这个文件夹下:
并且输入以下命令:
opencv_createsamples.exe -vec pos.vec -info posdata.txt -num 200 -w 20 -h 20
那个200代表着一共有200张正样本,两个20代表正样本大小为20*20,输入之后,我们会生成一个 pos.vec 文件。
H.在这之后,我们就可以进行训练了,进行训练的口令是
opencv_traincascade.exe -data xml -vec pos.vec -bg negdata.txt -numPos 100 -numNeg 350 -numStages 20 -w 20 -h 20 -mode ALL -mem 3000
在这里,我推荐 -numPos 与 -numNeg 的比值在 1:3 左右,这是一个比较好的比例;-mem 代表着你能给这段程序多少内存,由你的电脑而定。
要注意的是,训练之前我们一定要保持 xml 文件夹是一个空文件夹,否则会报错。如果训练正常,我们就会出现以下界面:
训练好之后,我们可以在xml文件夹中找到名为 cascade.xml 的文件,这就是训练好的分类器。在这里,这篇文章对训练中的参数做出总结,我觉得很好。
有关参数总结
在这之后,我们就可以使用我们训练好的分类器啦,就可以识别出特朗普喽:
有关的代码是:
// 行人识别.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
void main()
{
cv::CascadeClassifier face_detector;
face_detector.load("..\\cascade.xml");
if (face_detector.empty())
{
std::cout << "分类器加载失败!!!" << std::endl;
return;
}
cv::Size original_size = face_detector.getOriginalWindowSize();
cv::Mat image = cv::imread("..\\trump3.jpg", cv::IMREAD_COLOR);
if (image.empty())
{
std::cout << "图片加载失败!!!" << std::endl;
return;
}
cv::Mat image_gray;
cv::cvtColor(image, image_gray, cv::COLOR_BGR2GRAY);
std::vector faces;
face_detector.detectMultiScale(image_gray, faces, 1.05, 1, 0);
for (size_t i = 0; i < faces.size(); i++)
{
cv::rectangle(image, faces[i], cv::Scalar(0, 0, 255), 2, 8, 0);
}
cv::imshow("detect result", image);
printf("人脸数量为%d\n", faces.size());
cv::waitKey(0);
}