
#include "opencv2/imgproc.hpp" // 包含OpenCV图像处理头文件
#include "opencv2/highgui.hpp" // 包含OpenCV高层GUI(图形用户界面)头文件
#include "opencv2/ml.hpp" // 包含OpenCV机器学习模块头文件
#include "opencv2/objdetect.hpp"// 包含OpenCV目标检测模块头文件
#include "opencv2/videoio.hpp" // 包含OpenCV视频I/O模块头文件
#include <iostream> // 包含输入输出流头文件
#include <time.h> // 包含C标准库时间操作头文件
using namespace cv; // 使用cv命名空间
using namespace cv::ml; // 使用cv::ml命名空间
using namespace std; // 使用std命名空间
// 函数声明区
vector< float > get_svm_detector( const Ptr< SVM >& svm ); // 获取SVM检测器的函数声明
void convert_to_ml( const vector< Mat > & train_samples, Mat& trainData ); // 将训练样本转换为机器学习算法格式的函数声明
void load_images( const String & dirname, vector< Mat > & img_lst, bool showImages ); // 加载图片的函数声明
void sample_neg( const vector< Mat > & full_neg_lst, vector< Mat > & neg_lst, const Size & size ); // 获取负样本的函数声明
void computeHOGs( const Size wsize, const vector< Mat > & img_lst, vector< Mat > & gradient_lst, bool use_flip ); // 计算HOG特征的函数声明
void test_trained_detector( String obj_det_filename, String test_dir, String videofilename ); // 测试训练好的检测器的函数声明
// 获取SVM支持向量机的检测器
vector< float > get_svm_detector( const Ptr< SVM >& svm )
{
// 获取支持向量
Mat sv = svm->getSupportVectors();
const int sv_total = sv.rows;
// 获取决策函数
Mat alpha, svidx;
double rho = svm->getDecisionFunction( 0, alpha, svidx );
// 检查维度和数据类型是否一致
CV_Assert( alpha.total() == 1 && svidx.total() == 1 && sv_total == 1 );
CV_Assert( (alpha.type() == CV_64F && alpha.at<double>(0) == 1.) ||
(alpha.type() == CV_32F && alpha.at<float>(0) == 1.f) );
CV_Assert( sv.type() == CV_32F );
// 预备HOG检测器向量
vector< float > hog_detector( sv.cols + 1 );
memcpy( &hog_detector[0], sv.ptr(), sv.cols*sizeof( hog_detector[0] ) );
hog_detector[sv.cols] = (float)-rho; // 最后一个元素是偏移量rho
return hog_detector; // 返回HOG检测器
}
/*
* 将训练/测试集转换为OpenCV机器学习算法可以使用的格式。
* TrainData是一个矩阵,大小为(#samples x max(#cols,#rows) per sample),数据类型为32FC1。
* 如果需要,会进行样本的转置。
*/
void convert_to_ml( const vector< Mat > & train_samples, Mat& trainData )
{
// 转换数据
const int rows = (int)train_samples.size();
const int cols = (int)max( train_samples[0].cols, train_samples[0].rows );
Mat tmp( 1, cols, CV_32FC1 ); // 用于必要时的转置
trainData = Mat( rows, cols, CV_32FC1 );
// 遍历训练样本并将它们转换为一维特征向量
for( size_t i = 0 ; i < train_samples.size(); ++i )
{
// 确保每个训练样本是一维的
CV_Assert( train_samples[i].cols == 1 || train_samples[i].rows == 1 );
// 如果样本是列向量,需要进行转置
if( train_samples[i].cols == 1 )
{
transpose( train_samples[i], tmp );
tmp.copyTo( trainData.row( (int)i ) );
}
// 如果样本已经是行向量,直接复制
else if( train_samples[i].rows == 1 )
{
train_samples[i].copyTo( trainData.row( (int)i ) );
}
}
}
// 加载图片的函数实现
void load_images( const String & dirname, vector< Mat > & img_lst, bool showImages = false )
{
vector< String > files; // 文件名的字符串数组
glob( dirname, files ); // 读取目录下的所有文件名
// 遍历文件名,加载每张图片
for ( size_t i = 0; i < files.size(); ++i )
{
Mat img = imread( files[i] ); // 读取图片
// 若图片为空,则输出无效并继续
if ( img.empty() )
{
cout << files[i] << " is invalid!" << endl; // 如果图片无效,则输出错误信息
continue;
}
// 如果需要显示图片,则显示每一张载入的图片
if ( showImages )