如何用opencv在一周内实现人物行为语义识别

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用opencv在一周内实现人物行为语义识别相关的知识,希望对你有一定的参考价值。

 支持向量机在解决二分类问题方面有着强大的威力(当然也可以解决多分类问题),性别识别是典型的二分类模式识别问题,因此很适合用SVM进行处理,同时OpenCv又对SVM进行了很好的封装,调用非常方便,因此我们在这个性别识别程序中考虑加入SVM方法。

  在这里我们采用了HOG+SVM的模式来进行,即先提取图像的HOG特征,然后将这些HOG特征输入SVM中进行训练。

  一、SVM概述

  SVM的数学原理十分复杂,我们不在这里过多讨论,有关OpenCv中SVM的用法,这里为大家提供两篇博客以供参考:OpenCV的SVM用法以及OpenCV 2.4+ C++ SVM介绍。

  二、HOG特征概述

  HOG特征是图像的梯度特征,具体参见:目标检测的图像特征提取之(一)HOG特征

  三、建立训练集

  这里继续沿用上一篇博文中提到的性别识别训练集,400张男性人脸样本400张女性人脸样本,下载地址:性别识别数据集。

  四、算法的训练与测试

  1、建立控制台工程,配置OpenCv环境

  这里将工程命名为:GenderSVM。

  2、编写批量读取函数read_csv()

  只要涉及到训练,都需要批量读取训练样本的操作,SVM也不例外,因此需要先编写批量读取函数read_csv()。考虑到之前的批量读取函数必须一次性将所有训练样本读入内存中,内存消耗较大,在这里做一个小小的改进:

void read_csv(String& csvPath,Vector<String>& trainPath,Vector<int>& label,char separator = ';')

string line,path,classLabel;
ifstream file(csvPath.c_str(),ifstream::in);
while (getline(file,line))

stringstream lines(line);
getline(lines,path,separator);
getline(lines,classLabel);
if (!path.empty()&&!classLabel.empty())

trainPath.push_back(path);
label.push_back(atoi(classLabel.c_str()));




  可见这里我们将输入参数由vector<Mat>改为vector<String>,然后返回装有训练样本的所有路径的容器,需要时在根据其中的路径进行读取,降低了内存占用量。

  3、读入训练样本路径

string trainCsvPath = "E:\\性别识别数据库—CAS-PEAL\\at.txt";
vector<String> vecTrainPath;
vector<int> vecTrainLabel;
read_csv(trainCsvPath,vecTrainPath,vecTrainLabel);

  顺利批量读入路径:

  4、训练初始化

  在提取HOG特征之前,需要初始化训练数据矩阵:

/**********初始化训练数据矩阵**********/
int iNumTrain = 800;
Mat trainDataHog;
Mat trainLabel = Mat::zeros(iNumTrain,1,CV_32FC1);

  需要强调的是SVM的训练数据必须都是CV_32FC1格式,因此这里显式的将标签矩阵trainLabel初始化为CV_32FC1格式,trainDataHog稍后进行初始化。

  5、提取图像HOG特征

  接下来循环读入所有的训练样本,提取HOG特征,放在训练数据矩阵中。考虑嵌套代码的复杂性,这里先给出整体代码,稍后解释:

/**********提取HOG特征,放入训练数据矩阵中**********/
Mat imageSrc;
for (int i = 0; i < iNumTrain; i++)

imageSrc = imread(vecTrainPath[i].c_str(),1);
resize(imageSrc,imageSrc,Size(64,64));
HOGDescriptor *hog = new HOGDescriptor(cvSize(64,64),cvSize(16,16),
cvSize(8,8),cvSize(8,8),9);
vector<float> descriptor;
hog->compute(imageSrc,descriptor,Size(1,1),Size(0,0));

if (i == 0)

trainDataHog = Mat::zeros(iNumTrain,descriptor.size(),CV_32FC1);


int n = 0;
for (vector<float>::iterator iter = descriptor.begin();iter != descriptor.end();iter++)

trainDataHog.at<float>(i,n) = *iter;
n++;

trainLabel.at<float>(i,0) = vecTrainLabel[i];


  接下来我们对这段代码进行详细解释。

  (1)循环读入训练样本

  从vecTrainPath容器中逐条取出训练样本路径,然后读取:

imageSrc = imread(vecTrainPath[i].c_str(),1);

  (2)尺寸归一化

  我们这里将图像尺寸归一化为64*64,这是因为当时在写程序时参考了一篇关于HOG特征的博客。这里的尺寸大家可以随意设定,当然也会影响最终的识别效率,64*64可能并不是一个最优的尺寸:

imageSrc = imread(vecTrainPath[i].c_str(),1);
resize(imageSrc,imageSrc,Size(64,64));

  (3)计算HOG特征

  OpenCv给出的HOG特征计算接口非常简洁,三句话即完成:

HOGDescriptor *hog = new HOGDescriptor(cvSize(64,64),cvSize(16,16),
cvSize(8,8),cvSize(8,8),9);
vector<float> descriptor;
hog->compute(imageSrc,descriptor,Size(1,1),Size(0,0));

  提取的特征以容器的数据 结构形式给出。至于计算时的参数设定,参见我之前提供的那两篇博客即可。

  (4)初始化数据矩阵trainDataHog

  前面提到,SVM中用到的训练数据矩阵必须是CV_32FLOAT形式的,因此需要对数据矩阵显示的指定其尺寸和类型。然后由于trainDataHog行数为训练样本个数,而列数为图片HOG特征的维数,因此无法在进行HOG特征提取之前确定其尺寸,因此这里选择在进行完第一张样本的HOG特征、得到对应维数之后,在进行初始化:

if (i == 0)

trainDataHog = Mat::zeros(iNumTrain,descriptor.size(),CV_32FC1);


  (5)将得到的HOG特征存入数据矩阵

  得到的HOG特征是浮点数容器的形式,我们需要将其转换成矩阵的形式以便于训练SVM,这就涉及到了vector和Mat两个数据结构的遍历。vector遍历这里推荐使用迭代器的方式,而Mat遍历这里则选择了相对耗时但是最简单的方式——直接使用at函数:

int n = 0;
for (vector<float>::iterator iter = descriptor.begin();iter != descriptor.end();iter++)

trainDataHog.at<float>(i,n) = *iter;
n++;

trainLabel.at<float>(i,0) = vecTrainLabel[i];

  训练得到的HOG特征如图所示:

  可见在当前的参数设定下,提取到的HOG特征为1764维,共800张训练样本,每一行代表一个图片的HOG特征向量。通过“ctrl+鼠标滚轮”放大观察特征向量的具体参数:

  6、训练SVM分类器

  有关OpenCv中SVM分类器的使用可以参见以下博客:OpenCV 2.4+ C++ SVM介绍。

  首先,初始化相关参数:

/**********初始化SVM分类器**********/
CvSVM svm;
CvSVMParams param;
CvTermCriteria criteria;
criteria = cvTermCriteria( CV_TERMCRIT_EPS, 1000, FLT_EPSILON );
param = CvSVMParams(CvSVM::C_SVC, CvSVM::RBF,
10.0, 0.09, 1.0, 10.0, 0.5, 1.0, NULL, criteria );

  开始训练、训练完成后保存分类器:

/**********训练并保存SVM**********/
svm.train(trainDataHog,trainLabel,Mat(),Mat(),param);
svm.save("E:\\性别识别数据库—CAS-PEAL\\SVM_SEX_Model.txt");

  注意我们这里选择将分类器保存为txt形式:

  当然,我们可以打开这个txt文件,查看里面的参数:

  7、测试分类效果

  测试过程和训练过程基本相同,读取图片、尺寸归一化、提取HOG特征、预测:

/**********测试SVM分类性能**********/
Mat testImage = imread("E:\\性别识别数据库—CAS-PEAL\\测试样本\\女性测试样本\\face_35.bmp");
resize(testImage,testImage,Size(64,64));

HOGDescriptor *hog = new HOGDescriptor(cvSize(64,64),cvSize(16,16),
cvSize(8,8),cvSize(8,8),9);
vector<float> descriptor;
hog->compute(testImage,descriptor,Size(1,1),Size(0,0));

Mat testHog = Mat::zeros(1,descriptor.size(),CV_32FC1);
int n = 0;
for (vector<float>::iterator iter = descriptor.begin();iter != descriptor.end();iter++)

testHog.at<float>(0,n) = *iter;
n++;


int predictResult = svm.predict(testHog);

  8、完整代码

 这里给出HOG+SVM进行性别识别的完整代码:

// GenderSVM.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <opencv2\opencv.hpp>
#include <iostream>
#include <sstream>
#include <fstream>

using namespace std;
using namespace cv;

void read_csv(String& csvPath,vector<String>& trainPath,vector<int>& label,char separator = ';')

string line,path,classLabel;
ifstream file(csvPath.c_str(),ifstream::in);
while (getline(file,line))

stringstream lines(line);
getline(lines,path,separator);
getline(lines,classLabel);
if (!path.empty()&&!classLabel.empty())

trainPath.push_back(path);
label.push_back(atoi(classLabel.c_str()));




int _tmain(int argc, _TCHAR* argv[])

/**********批量读入训练样本路径**********/
string trainCsvPath = "E:\\性别识别数据库—CAS-PEAL\\at.txt";
vector<String> vecTrainPath;
vector<int> vecTrainLabel;
read_csv(trainCsvPath,vecTrainPath,vecTrainLabel);

/**********初始化训练数据矩阵**********/
int iNumTrain = 800;
Mat trainDataHog;
Mat trainLabel = Mat::zeros(iNumTrain,1,CV_32FC1);

/**********提取HOG特征,放入训练数据矩阵中**********/
Mat imageSrc;
for (int i = 0; i < iNumTrain; i++)

imageSrc = imread(vecTrainPath[i].c_str(),1);
resize(imageSrc,imageSrc,Size(64,64));
HOGDescriptor *hog = new HOGDescriptor(cvSize(64,64),cvSize(16,16),
cvSize(8,8),cvSize(8,8),9);
vector<float> descriptor;
hog->compute(imageSrc,descriptor,Size(1,1),Size(0,0));

if (i == 0)

trainDataHog = Mat::zeros(iNumTrain,descriptor.size(),CV_32FC1);


int n = 0;
for (vector<float>::iterator iter = descriptor.begin();iter != descriptor.end();iter++)

trainDataHog.at<float>(i,n) = *iter;
n++;

trainLabel.at<float>(i,0) = vecTrainLabel[i];


/**********初始化SVM分类器**********/
CvSVM svm;
CvSVMParams param;
CvTermCriteria criteria;
criteria = cvTermCriteria( CV_TERMCRIT_EPS, 1000, FLT_EPSILON );
param = CvSVMParams(CvSVM::C_SVC, CvSVM::RBF,
10.0, 0.09, 1.0, 10.0, 0.5, 1.0, NULL, criteria );

/**********训练并保存SVM**********/
svm.train(trainDataHoghttp://www.yingtaow.com?trainLabel,Mat(),Mat(),param);
svm.save("E:\\性别识别数据库—CAS-PEAL\\SVM_SEX_Model.txt");

/**********测试SVM分类性能**********/
Mat testImage = imread("E:\\性别识别数据库—CAS-PEAL\\测试样本\\女性测试样本\\face_35.bmp");
resize(testImage,testImage,Size(64,64));

HOGDescriptor *hog = new HOGDescriptor(cvSize(64,64),cvSize(16,16),
cvSize(8,8),cvSize(8,8),9);
vector<float> descriptor;
hog->compute(testImage,descriptor,Size(1,1),Size(0,0));

Mat testHog = Mat::zeros(1,descriptor.size(),CV_32FC1);
int n = 0;
for (vector<float>::iterator iter = descriptor.begin();iter != descriptor.end();iter++)

testHog.at<float>(0,n) = *iter;
n++;

int predictResult = svm.predict(testHog);

return 0;


  五、总结

  以上就是通过HOG特征+SVM进行性别识别的完整代码,在编写代码的过程中遇到了一些有趣的问题,这里稍作总结。

  1、变量命名格式

  当代码量很大的时候,变量的命名格式就显得十分重要,相信大家早已不用那种a、b、m、n这种简单的无意义的命名方法了。在C++中推荐大家使用匈牙利命名法,即“类型缩写+变量名缩写”的命名格式。例如vecTrainPath这个变量名,前缀“vec”表明这个变量是一个vector格式的变量,而“TrainPath”则表明这个容器中存放的是训练样本的路径。这种命名方式在大型工程中非常重要,还有一点需要注意的是当变量名中出现多个缩略短语时,推荐第一个短语小写,其他短语的首字母大写。

  2、为何选择HOG特征

  通过实验发现,直接将图像向量化后输入SVM(不经过特征提取)的方式的正确率将不理想。虽然本质上像素本身最能代表图像的语义信息,但由于SVM并不具备特征提取能力,因此效果不佳。确切的说,特征提取是模式分类的必要过程,即便是深度学习也不例外,因为深度学习(DeepLearning)本质上也是一种特征提取的手段,只不过提取得到的特征更深层,更抽象,表现力更强。为此我之前曾专门写过一篇博客进行阐述:浅谈模式识别中的特征提取

  当然这里大家可以尝试提取其他特征之后再进行分类,甚至可以考虑通过提起深度特征来进行分类,这里只是以HOG特征为例而已。

  4、有关vector的一些使用(为什么不用int型数组)

  在这段代码中我们大量用到了vector结构,这是C++11的新特性。仔细观察,其实vector结构的最明显的一个优势就是能够动态分配大小,实时添加/删除元素,这点是数组所不能实现的。虽然可以通过new操作符来实现数组的动态分配,但我们仍推荐大家在需要使用可动态变化的数组的场合,使用vector。
参考技术A 背景提取可以用GMM(高斯混合模型)+形态学处理,虽然时间复杂度比较大但是效果很好。但我之前写的代码都是Matlab的,opencv不熟悉,不过在stack overflow之类的网站上搜一下应该不难找到opencv版代码。

初学者如何在一周内熟练掌握python?

希望在一周内学习最强大和最流行的编程语言之一。

是的,你读得对,如果你有奉献精神,你可以在一周内学习Python。

沿着下面的路走。最后,如果你的时间不是很紧张,并且又想python快速的提高,最重要的是不怕吃苦,建议你可以扩我伪xin 762459510 ,那个真的很不错,很多人进步都很快,需要你不怕吃苦哦!大家可以去添加上看一下~

这是一条值得学习的道路。Python一周之内。

所以让我们从一天一天开始。

入门Python其实很容易,但是我们要去坚持学习,每一天坚持很困难,我相信很多人学了一个星期就放弃了,为什么呢?其实没有好的学习资料给你去学习,你们是很难坚持的,这是小编收集的Python入门学习资料关注,转发,私信小编“01”,即可免费领取!希望对你们有帮助

第一天

基本概念(3小时)

变量

显示输出(打印命令)

接受用户输入(输入命令)

条件语句

第二天

4个基本概念(3小时)

列表(索引、追加、删除)

用于循环

同时循环

功能

你可以从这里学到基本知识–

Python基础

第03天

简单的编码问题(5小时)

将英里转换为公里

计算简单利息

查找列表中最大的数字

检查一个数字是否是素数

数字中所有数字的和

反向字符串(检查回文)

计算年龄

构建一个简单的计算器

第04天

数据结构(3小时)

堆栈和队列

字典

元组

你可以从这里学到DS

学习数据结构

第五天

OOP(4小时)

对象

班级

方法与构造器

OOP-继承

为此,你可以看YouTube视频

第06天

算法(4小时)

二进制搜索

气泡分类

选择排序

我见过的最好的算法课程之一–

学习算法

第07天

项目(6小时)

构建一个网络爬虫

或者新闻聚合器

或者一个简单的游戏使用游戏

瞧!!你成功了,你现在初级Python开发人员。

所以,这就是现在希望你发现它有用。

现在全靠你了。

祝你好运!!

以上是关于如何用opencv在一周内实现人物行为语义识别的主要内容,如果未能解决你的问题,请参考以下文章

初学者如何在一周内熟练掌握python?

我是如何在一周内拿到4份offer的?

我是如何在一周内拿到4份offer的?

mysql中怎么查询一周内,三个月内,半年内的数据?

如何用opencv实现人脸检测与跟踪

如何用 OpenCVPython 和深度学习实现面部识别?