网站建设售后服务承诺书,做网站竞争者的优势,大连网站快速建设推荐,如何用ad做网站1. HOG特征简介
特征描述符是图像或图像补丁的表示形式#xff0c;它通过提取有用信息并丢弃无关信息来简化图像。
通常#xff0c;特征描述符将大小W x H x 3#xff08;通道#xff09;的图像转换为长度为n的特征向量/数组。对于 HOG 特征描述符#xff0c;输入图像的…1. HOG特征简介
特征描述符是图像或图像补丁的表示形式它通过提取有用信息并丢弃无关信息来简化图像。
通常特征描述符将大小W x H x 3通道的图像转换为长度为n的特征向量/数组。对于 HOG 特征描述符输入图像的大小为 64 x 128 x 3输出特征向量的长度为 3780。在HOG特征描述符中梯度方向的分布直方图被用作特征。图像的渐变x和y导数很有用因为边缘和角落强度突然变化的区域周围的梯度大小很大我们知道边缘和角落比平面区域包含更多关于物体形状的信息。HOGHistogram of Oriented Gridients的简写特征检测算法最早是由法国研究员Dalal等在CVPR-2005上提出来的一种解决人体目标检测的图像描述子是一种用于表征图像局部梯度方向和梯度强度分布特性的描述符。其主要思想是在边缘具体位置未知的情况下边缘方向的分布也可以很好的表示行人目标的外形轮廓。HOG特征检测算法的几个步骤图像预处理—梯度计算—梯度方向直方图—重叠块直方图归一化—HOG特征。下面分别对其进行介绍。
2. HOG算法实现
2.1 图像预处理 图像缩放 用于行人检测的 HOG 特征描述符是在图像的 64×128 个尺寸上计算的。当然图像可以是任何大小。通常在许多图像位置分析多个尺度的斑块。唯一的限制是正在分析的修补程序具有固定的纵横比。在我们的例子中补丁需要具有 12 的纵横比。例如它们可以是 100×200、128×256 或 1000×2000但不能是 101×205。 灰度化 对于彩色图像可以将将RGB分量转化成灰度图像其转化公式为 伽马校正 在图像照度不均匀的情况下可以通过Gamma校正将图像整体亮度提高或降低。在实际中可以采用两种不同的方式进行Gamma标准化平方根、对数法。这里我们采用平方根的办法公式如下其中γ0.5
2.2 计算梯度图像
注下边步骤省略灰度化、伽马变化过程以行人的彩色图像为例计算HOG特征 要计算 HOG 描述符我们需要首先计算水平和垂直梯度;毕竟我们要计算梯度的直方图。这可以通过使用以下内核过滤图像轻松实现。
我们也可以通过在内核大小为 1 的 OpenCV 中使用 Sobel 运算符来实现相同的结果
// C gradient calculation.
// Read image
Mat img imread(bolt.png);
img.convertTo(img, CV_32F, 1/255.0);// Calculate gradients gx, gy
Mat gx, gy;
Sobel(img, gx, CV_32F, 1, 0, 1);
Sobel(img, gy, CV_32F, 0, 1, 1);# Python gradient calculation # Read image
im cv2.imread(bolt.png)
im np.float32(im) / 255.0# Calculate gradient
gx cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize1)
gy cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize1)接下来我们可以使用以下公式找到梯度的大小和方向
如果您使用的是OpenCV则可以使用函数cartToPolar完成计算如下所示
// C Calculate gradient magnitude and direction (in degrees)
Mat mag, angle;
cartToPolar(gx, gy, mag, angle, 1);Python代码如下
# Python Calculate gradient magnitude and direction ( in degrees )
mag, angle cv2.cartToPolar(gx, gy, angleInDegreesTrue)下图显示了渐变 在每个像素处梯度都有一个大小和一个方向。对于彩色图像将评估三个通道的梯度如上图所示。一个像素处的梯度大小是三个通道梯度大小的最大值角度是最大梯度对应的角度。
2.3 计算8×8个单元格中的梯度直方图
为什么是8×8补丁为什么不是 32×32 这是一个由我们正在寻找的功能规模决定的设计选择。HOG最初用于行人检测。在一张比例为8×8的行人照片中64×128个单元格足够大可以捕捉有趣的特征例如面部头顶等。 下一步是在这 8×8 个单元格中创建梯度直方图。直方图包含 9 个箱对应于角度 0、20、40 …160. 下图说明了该过程。我们正在研究与上图相同的 8×8 补丁的梯度的大小和方向。根据方向选择箱并根据大小选择投票进入箱的值。让我们首先关注用蓝色包围的像素。它的角度方向为80度星等为2。因此它将 2 添加到第 5 个箱中。使用红色包围的像素处的梯度角度为 10 度星等为 4。由于 10 度介于 0 和 20 之间因此像素的投票将均匀地分成两个箱。 还有一个细节需要注意。如果角度大于 160 度则介于 160 和 180 之间我们知道角度环绕使 0 和 180 等效。因此在下面的示例中角度为 165 度的像素按比例贡献 0 度箱和 160 度箱。 将 8×8 个单元格中所有像素的贡献相加以创建 9 箱直方图。对于上面的尺寸它看起来像这样 在我们的表示中y 轴为 0 度。您可以看到直方图在 0 度和 180 度附近有很多权重这只是另一种说法在色块中梯度指向向上或向下。
2.4 16×16 块规范化 理想情况下我们希望描述符独立于光照变化。换句话说我们希望对直方图进行“归一化”以便它们不受光照变化的影响。 如上图所示一个 16×16 块有 4 个直方图可以连接起来形成一个 36 x 1 元素向量并且可以像 3×1 向量归一化一样进行归一化。然后窗口移动8个像素参见动画并在此窗口上计算归一化的36×1向量并重复该过程。其中归一化实现为分别对每个block进行标准化一个block内有4个cell每个cell含9维特征向量故每个block就由4x936维特征向量来表征。
2.5 HOG特征 HOG特征即计算定向梯度特征向量的直方图 为了计算整个图像块的最终特征向量将 36×1 个向量连接成一个巨大的向量。这个向量的大小是多少让我们计算一下 16×16 个区块有多少个仓位有 7 个水平位置和 15 个垂直位置总共 7 x 15 105 个位置。每个 16×16 块由 36×1 向量表示。因此当我们将它们全部连接成一个增益向量时我们得到一个 36×105 3780 维向量。 计算结果可视化如下 3. Opencv中HOG特征使用
Opencv官方API参考https://docs.opencv.org/4.x/d5/d33/structcv_1_1HOGDescriptor.html附一个Opencv官方使用教程
#include opencv2/imgproc.hpp
#include opencv2/highgui.hpp
#include opencv2/ml.hpp
#include opencv2/objdetect.hpp
#include opencv2/videoio.hpp
#include iostream
#include time.h
using namespace cv;
using namespace cv::ml;
using namespace std;
vector float get_svm_detector( const Ptr SVM svm );
void convert_to_ml( const std::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 );
void test_trained_detector( String obj_det_filename, String test_dir, String videofilename );
vector float get_svm_detector( const Ptr SVM svm )
{// get the support vectorsMat sv svm-getSupportVectors();const int sv_total sv.rows;// get the decision functionMat 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.atdouble(0) 1.) ||(alpha.type() CV_32F alpha.atfloat(0) 1.f) );CV_Assert( sv.type() CV_32F );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;return hog_detector;
}
/*
* Convert training/testing set to be used by OpenCV Machine Learning algorithms.
* TrainData is a matrix of size (#samples x max(#cols,#rows) per samples), in 32FC1.
* Transposition of samples are made if needed.
*/
void convert_to_ml( const vector Mat train_samples, Mat trainData )
{//--Convert dataconst int rows (int)train_samples.size();const int cols (int)std::max( train_samples[0].cols, train_samples[0].rows );Mat tmp( 1, cols, CV_32FC1 ); // used for transposition if neededtrainData 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] ); // load the imageif ( img.empty() ){cout files[i] is invalid! endl; // invalid image, skip it.continue;}if ( showImages ){imshow( image, img );waitKey( 1 );}img_lst.push_back( img );}
}
void sample_neg( const vector Mat full_neg_lst, vector Mat neg_lst, const Size size )
{Rect box;box.width size.width;box.height size.height;srand( (unsigned int)time( NULL ) );for ( size_t i 0; i full_neg_lst.size(); i )if ( full_neg_lst[i].cols box.width full_neg_lst[i].rows box.height ){box.x rand() % ( full_neg_lst[i].cols - box.width );box.y rand() % ( full_neg_lst[i].rows - box.height );Mat roi full_neg_lst[i]( box );neg_lst.push_back( roi.clone() );}
}
void computeHOGs( const Size wsize, const vector Mat img_lst, vector Mat gradient_lst, bool use_flip )
{HOGDescriptor hog;hog.winSize wsize;Mat gray;vector float descriptors;for( size_t i 0 ; i img_lst.size(); i ){if ( img_lst[i].cols wsize.width img_lst[i].rows wsize.height ){Rect r Rect(( img_lst[i].cols - wsize.width ) / 2,( img_lst[i].rows - wsize.height ) / 2,wsize.width,wsize.height);cvtColor( img_lst[i](r), gray, COLOR_BGR2GRAY );hog.compute( gray, descriptors, Size( 8, 8 ), Size( 0, 0 ) );gradient_lst.push_back( Mat( descriptors ).clone() );if ( use_flip ){flip( gray, gray, 1 );hog.compute( gray, descriptors, Size( 8, 8 ), Size( 0, 0 ) );gradient_lst.push_back( Mat( descriptors ).clone() );}}}
}
void test_trained_detector( String obj_det_filename, String test_dir, String videofilename )
{cout Testing trained detector... endl;HOGDescriptor hog;hog.load( obj_det_filename );vector String files;glob( test_dir, files );int delay 0;VideoCapture cap;if ( videofilename ! ){if ( videofilename.size() 1 isdigit( videofilename[0] ) )cap.open( videofilename[0] - 0 );elsecap.open( videofilename );}obj_det_filename testing obj_det_filename;namedWindow( obj_det_filename, WINDOW_NORMAL );for( size_t i0;; i ){Mat img;if ( cap.isOpened() ){cap img;delay 1;}else if( i files.size() ){img imread( files[i] );}if ( img.empty() ){return;}vector Rect detections;vector double foundWeights;hog.detectMultiScale( img, detections, foundWeights );for ( size_t j 0; j detections.size(); j ){Scalar color Scalar( 0, foundWeights[j] * foundWeights[j] * 200, 0 );rectangle( img, detections[j], color, img.cols / 400 1 );}imshow( obj_det_filename, img );if( waitKey( delay ) 27 ){return;}}
}
int main( int argc, char** argv )
{const char* keys {{help h| | show help message}{pd | | path of directory contains positive images}{nd | | path of directory contains negative images}{td | | path of directory contains test images}{tv | | test video file name}{dw | | width of the detector}{dh | | height of the detector}{f |false| indicates if the program will generate and use mirrored samples or not}{d |false| train twice}{t |false| test a trained detector}{v |false| visualize training steps}{fn |my_detector.yml| file name of trained SVM}};CommandLineParser parser( argc, argv, keys );if ( parser.has( help ) ){parser.printMessage();exit( 0 );}String pos_dir parser.get String ( pd );String neg_dir parser.get String ( nd );String test_dir parser.get String ( td );String obj_det_filename parser.get String ( fn );String videofilename parser.get String ( tv );int detector_width parser.get int ( dw );int detector_height parser.get int ( dh );bool test_detector parser.get bool ( t );bool train_twice parser.get bool ( d );bool visualization parser.get bool ( v );bool flip_samples parser.get bool ( f );if ( test_detector ){test_trained_detector( obj_det_filename, test_dir, videofilename );exit( 0 );}if( pos_dir.empty() || neg_dir.empty() ){parser.printMessage();cout Wrong number of parameters.\n\n Example command line:\n argv[0] -dw64 -dh128 -pd/INRIAPerson/96X160H96/Train/pos -nd/INRIAPerson/neg -td/INRIAPerson/Test/pos -fnHOGpedestrian64x128.xml -d\n \nExample command line for testing trained detector:\n argv[0] -t -fnHOGpedestrian64x128.xml -td/INRIAPerson/Test/pos;exit( 1 );}vector Mat pos_lst, full_neg_lst, neg_lst, gradient_lst;vector int labels;clog Positive images are being loaded... ;load_images( pos_dir, pos_lst, visualization );if ( pos_lst.size() 0 ){clog ...[done] pos_lst.size() files. endl;}else{clog no image in pos_dir endl;return 1;}Size pos_image_size pos_lst[0].size();if ( detector_width detector_height ){pos_image_size Size( detector_width, detector_height );}else{for ( size_t i 0; i pos_lst.size(); i ){if( pos_lst[i].size() ! pos_image_size ){cout All positive images should be same size! endl;exit( 1 );}}pos_image_size pos_image_size / 8 * 8;}clog Negative images are being loaded...;load_images( neg_dir, full_neg_lst, visualization );clog ...[done] full_neg_lst.size() files. endl;clog Negative images are being processed...;sample_neg( full_neg_lst, neg_lst, pos_image_size );clog ...[done] neg_lst.size() files. endl;clog Histogram of Gradients are being calculated for positive images...;computeHOGs( pos_image_size, pos_lst, gradient_lst, flip_samples );size_t positive_count gradient_lst.size();labels.assign( positive_count, 1 );clog ...[done] ( positive images count : positive_count ) endl;clog Histogram of Gradients are being calculated for negative images...;computeHOGs( pos_image_size, neg_lst, gradient_lst, flip_samples );size_t negative_count gradient_lst.size() - positive_count;labels.insert( labels.end(), negative_count, -1 );CV_Assert( positive_count labels.size() );clog ...[done] ( negative images count : negative_count ) endl;Mat train_data;convert_to_ml( gradient_lst, train_data );clog Training SVM...;Ptr SVM svm SVM::create();/* Default values to train SVM */svm-setCoef0( 0.0 );svm-setDegree( 3 );svm-setTermCriteria( TermCriteria(TermCriteria::MAX_ITER TermCriteria::EPS, 1000, 1e-3 ) );svm-setGamma( 0 );svm-setKernel( SVM::LINEAR );svm-setNu( 0.5 );svm-setP( 0.1 ); // for EPSILON_SVR, epsilon in loss function?svm-setC( 0.01 ); // From paper, soft classifiersvm-setType( SVM::EPS_SVR ); // C_SVC; // EPSILON_SVR; // may be also NU_SVR; // do regression tasksvm-train( train_data, ROW_SAMPLE, labels );clog ...[done] endl;if ( train_twice ){clog Testing trained detector on negative images. This might take a few minutes...;HOGDescriptor my_hog;my_hog.winSize pos_image_size;// Set the trained svm to my_hogmy_hog.setSVMDetector( get_svm_detector( svm ) );vector Rect detections;vector double foundWeights;for ( size_t i 0; i full_neg_lst.size(); i ){if ( full_neg_lst[i].cols pos_image_size.width full_neg_lst[i].rows pos_image_size.height )my_hog.detectMultiScale( full_neg_lst[i], detections, foundWeights );elsedetections.clear();for ( size_t j 0; j detections.size(); j ){Mat detection full_neg_lst[i]( detections[j] ).clone();resize( detection, detection, pos_image_size, 0, 0, INTER_LINEAR_EXACT);neg_lst.push_back( detection );}if ( visualization ){for ( size_t j 0; j detections.size(); j ){rectangle( full_neg_lst[i], detections[j], Scalar( 0, 255, 0 ), 2 );}imshow( testing trained detector on negative images, full_neg_lst[i] );waitKey( 5 );}}clog ...[done] endl;gradient_lst.clear();clog Histogram of Gradients are being calculated for positive images...;computeHOGs( pos_image_size, pos_lst, gradient_lst, flip_samples );positive_count gradient_lst.size();clog ...[done] ( positive count : positive_count ) endl;clog Histogram of Gradients are being calculated for negative images...;computeHOGs( pos_image_size, neg_lst, gradient_lst, flip_samples );negative_count gradient_lst.size() - positive_count;clog ...[done] ( negative count : negative_count ) endl;labels.clear();labels.assign(positive_count, 1);labels.insert(labels.end(), negative_count, -1);clog Training SVM again...;convert_to_ml( gradient_lst, train_data );svm-train( train_data, ROW_SAMPLE, labels );clog ...[done] endl;}HOGDescriptor hog;hog.winSize pos_image_size;hog.setSVMDetector( get_svm_detector( svm ) );hog.save( obj_det_filename );test_trained_detector( obj_det_filename, test_dir, videofilename );return 0;
}参考
1.HOG从理论到OpenCV实践 2.【特征检测】HOG特征算法