OpenCV实战【2】HOG+SVM实现行人检测

目录

    • HOG是什么?
    • HOG vs SIFT
    • HOG步骤
    • HOG在检测行人中的方式
    • Opencv实现
      • HOGDescriptor的构造函数:
      • 行人检测HOG+SVM步骤
    • 简化版的HOG计算

HOG是什么?

方向梯度直方图( Histogram of Oriented Gradient, HOG )特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过
计算和统计图像局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像识别中。

HOG vs SIFT

SIFT :对特征点的描述方法
HOG :对一定区域的特征量的描述方法
1、可以表现较大的形状
2、非常适合行人及车辆检测
假设我们在智能驾驶中要检测行人:
正样本:
行人
负样本:
负样本
识别的本质是要找到正样本和负样本最本质的区别。例如行人在肩部具有横向边缘、两臂具有竖向边缘。而非行人样本中的边缘是杂乱无章的。因此可以通过构建梯度的直方图来检测形状。由于直方图损失了空间信息,所以HOG将图像分割为一个一个小的区域(联系阈值处理中的分块处理法),对小的区域分别构建直方图,然后拼接得到一个大的直方图。
HOG的缺点: 速度慢,实时性差;难以处理遮挡问题。
HOG特征不具有旋转鲁棒性,以及尺度鲁棒性

HOG步骤

1、Gamma矫正(增强图像的对比度)
2、计算梯度信息
3、以cell(一个像素块)为单位计算梯度直方图
4、以block(几个cell为一个block)为单位,对特征量进行归一化
具体步骤:
在这里插入图片描述
一般来说对梯度方向进行九等分量化。
一般以3* 3的像素组成一个cell,这样每个cell就可以得到一个9维的直方图。
每 3*3个cell组成一个block,在每个block进行归一化:
block
归一化的目的:增强对亮度的鲁棒性。

HOG在检测行人中的方式

通常采用滑窗的方式:
1
计算滑窗中包含的像素的梯度直方图,然后与行人模板中的直方图进行对比(如利用各种矩),当两者十分相似时,我们就认为这个区域是行人区域。
从而延生出的问题:
由于模板是固定大小的,因此只能检测固定大小的行人。当图像中的行人尺寸发生变化时,如何使用一个单一的模板检测?

Opencv实现

OpenCV实现了两种类型的基于HOG特征的行人检测,分别是SVM和Cascade,OpenCV自带的级联分类器的文件的位置在“XX\opencv\sources\data\hogcascades”(OpenCV4.x版本可用)。
opencv自带的人数检测文件,所在位置在opencv的安装目录下(下面是我的安装位置):
D:\Program Files\opencv\sources\samples\cpp

HOGDescriptor的构造函数:

 CV_WRAP HOGDescriptor() : winSize(64,128), blockSize(16,16), blockStride(8,8),cellSize(8,8), nbins(9), derivAperture(1), winSigma(-1),histogramNormType(HOGDescriptor::L2Hys), L2HysThreshold(0.2), gammaCorrection(true),free_coef(-1.f), nlevels(HOGDescriptor::DEFAULT_NLEVELS), signedGradient(false){}

窗口大小 winSize(64,128), 块大小blockSize(16,16), 块滑动增量blockStride(8,8), 胞元大小cellSize(8,8), 梯度方向数nbins(9)。
上面这些都是HOGDescriptor的成员变量,括号里的数值是它们的默认值,它们反应了HOG描述子的参数。

nBins表示在一个胞元(cell)中统计梯度的方向数目,例如nBins=9时,在一个胞元内统计9个方向的梯度直方图,每个方向为180/9=20度。

HOGDescriptor中有两种Detector分别是:getDaimlerPeopleDetector、getDefaultPeopleDetector

行人检测HOG+SVM步骤

参考的代码:

#include <opencv2/objdetect.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <iostream>
#include <iomanip>using namespace cv;
using namespace std;class Detector
{//enum Mode { Default, Daimler } m;enum { Default, Daimler };//定义枚举类型int m;HOGDescriptor hog, hog_d;
public:Detector(int a) : m(a), hog(), hog_d(Size(48, 96), Size(16, 16), Size(8, 8), Size(8, 8), 9)//构造函数,初始化对象时自动调用,m,hog,hog_d是数据成员,后跟一个放在圆括号中的初始化形式{hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());hog_d.setSVMDetector(HOGDescriptor::getDaimlerPeopleDetector());}void toggleMode() { m = (m == Default ? Daimler : Default); }string modeName() const { return (m == Default ? "Default" : "Daimler"); }vector<Rect> detect(InputArray img){// Run the detector with default parameters. to get a higher hit-rate// (and more false alarms, respectively), decrease the hitThreshold and// groupThreshold (set groupThreshold to 0 to turn off the grouping completely).vector<Rect> found;if (m == Default)hog.detectMultiScale(img, found, 0, Size(8, 8), Size(32, 32), 1.05, 2, false);else if (m == Daimler)hog_d.detectMultiScale(img, found, 0.5, Size(8, 8), Size(32, 32), 1.05, 2, true);return found;}void adjustRect(Rect& r) const{// The HOG detector returns slightly larger rectangles than the real objects,// so we slightly shrink the rectangles to get a nicer output.r.x += cvRound(r.width * 0.1);r.width = cvRound(r.width * 0.8);r.y += cvRound(r.height * 0.07);r.height = cvRound(r.height * 0.8);}
};
//修改参数区域
static const string keys = "{ help h   |   | print help message }"
"{ camera c | 0 | capture video from camera (device index starting from 0) }"
"{ video v  | D:/opencv/opencv4.0/opencv4.0.0/sources/samples/data/vtest.avi| use video as input }";int main(int argc, char** argv)
{CommandLineParser parser(argc, argv, keys);		//keys:描述可接受的命令行参数的字符串parser.about("This sample demonstrates the use ot the HoG descriptor.");//设置相关信息。相关信息会在 printMessage 被调用时显示。if (parser.has("help")){parser.printMessage();return 0;}int camera = parser.get<int>("camera");string file = parser.get<string>("video");if (!parser.check())//检查解析错误。当错误发生时返回true。错误可能是转换错误、丢失参数等。{parser.printErrors();return 1;}VideoCapture cap;if (file.empty())cap.open(camera);elsecap.open(file.c_str());if (!cap.isOpened()){cout << "Can not open video stream: '" << (file.empty() ? "<camera>" : file) << "'" << endl;return 2;}cout << "Press 'q' or <ESC> to quit." << endl;cout << "Press <space> to toggle between Default and Daimler detector" << endl;//Default and Daimler detectorDetector detector(1);		//初始化使用Daimler detectorMat frame;for (;;){cap >> frame;if (frame.empty()){cout << "Finished reading: empty frame" << endl;break;}int64 t = getTickCount();vector<Rect> found = detector.detect(frame);t = getTickCount() - t;// show the window{ostringstream buf;buf << "Mode: " << detector.modeName() << " ||| "<< "FPS: " << fixed << setprecision(1) << (getTickFrequency() / (double)t);putText(frame, buf.str(), Point(10, 30), FONT_HERSHEY_PLAIN, 2.0, Scalar(0, 0, 255), 2, LINE_AA);}for (vector<Rect>::iterator i = found.begin(); i != found.end(); ++i){Rect& r = *i;detector.adjustRect(r);rectangle(frame, r.tl(), r.br(), cv::Scalar(0, 255, 0), 2);}imshow("People detector", frame);// interact with userconst char key = (char)waitKey(30);if (key == 27 || key == 'q') // ESC{cout << "Exit requested" << endl;break;}else if (key == ' '){detector.toggleMode();}}return 0;
}

简化后的对单张图片的检测

#include <opencv2/objdetect.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <iostream>
#include <iomanip>using namespace cv;
using namespace std;class Detector
{//enum Mode { Default, Daimler } m;enum { Default, Daimler };//定义枚举类型int m;HOGDescriptor hog, hog_d;
public:Detector(int a) : m(a), hog(), hog_d(Size(48, 96), Size(16, 16), Size(8, 8), Size(8, 8), 9)//构造函数,初始化对象时自动调用,m,hog,hog_d是数据成员,后跟一个放在圆括号中的初始化形式{hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());hog_d.setSVMDetector(HOGDescriptor::getDaimlerPeopleDetector());}void toggleMode() { m = (m == Default ? Daimler : Default); }string modeName() const { return (m == Default ? "Default" : "Daimler"); }vector<Rect> detect(InputArray img){// Run the detector with default parameters. to get a higher hit-rate// (and more false alarms, respectively), decrease the hitThreshold and// groupThreshold (set groupThreshold to 0 to turn off the grouping completely).vector<Rect> found;if (m == Default)hog.detectMultiScale(img, found, 0, Size(8, 8), Size(32, 32), 1.05, 2, false);else if (m == Daimler)hog_d.detectMultiScale(img, found, 0.5, Size(8, 8), Size(32, 32), 1.05, 2, true);return found;}void adjustRect(Rect& r) const{// The HOG detector returns slightly larger rectangles than the real objects,// so we slightly shrink the rectangles to get a nicer output.r.x += cvRound(r.width * 0.1);r.width = cvRound(r.width * 0.8);r.y += cvRound(r.height * 0.07);r.height = cvRound(r.height * 0.8);}
};int main(int argc, char** argv)
{Detector detector(1);		//初始化使用Daimler detectorMat img=imread("D:\\opencv_picture_test\\HOG行人检测\\timg.jpg");vector<Rect> found = detector.detect(img);for (vector<Rect>::iterator i = found.begin(); i != found.end(); ++i){Rect& r = *i;detector.adjustRect(r);		rectangle(img, r.tl(), r.br(), cv::Scalar(0, 255, 0), 2);}imshow("People detector", img);waitKey(0);return 0;
}

结果:
显示

简化版的HOG计算

步骤
需要用到的知识点:
1
曼哈顿距离:
曼哈顿距离
由于一般的建立数组的方法在【】中填变量是行不通的,这里我们采用动态建立数组的方法。在程序返回前必须将内存释放
动态建立数组

#include <opencv2/opencv.hpp>
#include "opencv2/features2d.hpp"
#include <iostream>
#include "windows.h"
#include <stdio.h>
#include <time.h>
#include <math.h>  
//#include "My_ImageProssing_base.h"
#define WINDOW_NAME1 "【程序窗口1】"			
#define WINDOW_NAME2 "【程序窗口2】"	
using namespace cv;
using namespace std;
RNG g_rng(12345);Mat src_image;
Mat img1;
Mat img2;//*--------------------------手动实现HOG描述子-------------------------------------*/
int angle_lianghua(float angle)
{int result = angle/45;return result;
}
int main()
{//改变控制台字体颜色system("color 02");//读取图像src_image = imread("D:\\opencv_picture_test\\HOG行人检测\\hogTemplate.jpg");img1 = imread("D:\\opencv_picture_test\\HOG行人检测\\img1.jpg");img2 = imread("D:\\opencv_picture_test\\HOG行人检测\\img2.jpg");//出错判断if (!(src_image.data || img1.data || img2.data)){cout << "image load failed!" << endl;return -1;}//【1】计算hogTemplate//所有像素计算梯度和角度方向Mat gx, gy;Mat mag, angle;	//幅值和角度Sobel(src_image, gx, CV_32F, 1, 0, 1);Sobel(src_image, gy, CV_32F, 0, 1, 1);cartToPolar(gx, gy, mag, angle, false);		//false获得的是角度int cellSize = 16;		//每个cell的大小int nx = src_image.cols / cellSize;	//每行有几个int ny = src_image.rows / cellSize;	//每列有几个int cellnums = nx * ny;	//有几个cellint bins = cellnums * 8;float* ref_hist = new float[bins];memset(ref_hist, 0, sizeof(float) * bins);int binnum = 0;//计算一张图for (int j = 0;j < ny;j++){for (int i = 0;i < nx;i++){//计算每个cell的直方图for (int y = j * cellSize;y < (j + 1) * cellSize;y++){for (int x = i * cellSize;x < (i + 1) * cellSize;x++){//对角度进行量化int tempangle1 = 0;float tempangle2 = angle.at<float>(y, x);	//当前像素的角度值tempangle1 = angle_lianghua(tempangle2);	//当前cell的角度分量float magnitude = mag.at<float>(y, x);		//当前像素的幅度值ref_hist[tempangle1 + binnum * 8] += magnitude;				//在数组中加上当前的}}binnum++;	//cell数目+1}}//【2】计算img1//所有像素计算梯度和角度方向Mat gx_img1, gy_img1;Mat mag_img1, angle_img1;	//幅值和角度Sobel(img1, gx_img1, CV_32F, 1, 0, 1);Sobel(img1, gy_img1, CV_32F, 0, 1, 1);cartToPolar(gx_img1, gy_img1, mag_img1, angle_img1, false);		//false获得的是角度nx = img1.cols / cellSize;	//每行有几个ny = img1.rows / cellSize;	//每列有几个cellnums = nx * ny;	//有几个cellbins = cellnums * 8;float* ref_hist_img1 = new float[bins];memset(ref_hist_img1, 0, sizeof(float) * bins);binnum = 0;//计算一张图for (int j = 0;j < ny;j++){for (int i = 0;i < nx;i++){//计算每个cell的直方图for (int y = j * cellSize;y < (j + 1) * cellSize;y++){for (int x = i * cellSize;x < (i + 1) * cellSize;x++){//对角度进行量化int tempangle1 = 0;float tempangle2 = angle_img1.at<float>(y, x);	//当前像素的角度值tempangle1 = angle_lianghua(tempangle2);	//当前cell的角度分量float magnitude = mag_img1.at<float>(y, x);		//当前像素的幅度值ref_hist_img1[tempangle1 + binnum * 8] += magnitude;				//在数组中加上当前的}}binnum++;	//cell数目+1}}//【3】计算img2//所有像素计算梯度和角度方向Mat gx_img2, gy_img2;Mat mag_img2, angle_img2;	//幅值和角度Sobel(img2, gx_img2, CV_32F, 1, 0, 1);Sobel(img2, gy_img2, CV_32F, 0, 1, 1);cartToPolar(gx_img2, gy_img2, mag_img2, angle_img2, false);		//false获得的是角度nx = img2.cols / cellSize;	//每行有几个ny = img2.rows / cellSize;	//每列有几个cellnums = nx * ny;	//有几个cellbins = cellnums * 8;float* ref_hist_img2 = new float[bins];memset(ref_hist_img2, 0, sizeof(float) * bins);binnum = 0;//计算一张图for (int j = 0;j < ny;j++){for (int i = 0;i < nx;i++){//计算每个cell的直方图for (int y = j * cellSize;y < (j + 1) * cellSize;y++){for (int x = i * cellSize;x < (i + 1) * cellSize;x++){//对角度进行量化int tempangle1 = 0;float tempangle2 = angle_img2.at<float>(y, x);	//当前像素的角度值tempangle1 = angle_lianghua(tempangle2);	//当前像素的角度分量float magnitude = mag_img2.at<float>(y, x);		//当前像素的幅度值ref_hist_img2[tempangle1 + binnum * 8] += magnitude;				//在数组中加上当前的}}binnum++;	//cell数目+1}}//【4】分别计算ref_hist_img1和ref_hist\ref_hist_img2和ref_hist的矩int result1 = 0;int result2 = 0;for (int i = 0;i < bins;i++){//这里简化运算,不计算平方根,而是计算absresult1 += abs(ref_hist[i]- ref_hist_img1[i]);result2 += abs(ref_hist[i] - ref_hist_img2[i]);}cout << result1 << endl;cout << result2 << endl;if (result1 < result2){cout << "img1更与原图相似" << endl;}elsecout << "img2更与原图相似" << endl;waitKey(0);delete[] ref_hist;delete[] ref_hist_img1;delete[] ref_hist_img2;return 0;
}

结果:
结果


Reference:

OpenCV实战4: HOG+SVM实现行人检测
HOG detectMultiScale 参数分析
CommandLineParser类(命令行解析类)
C++语法:构造函数以及析构函数
《数字图像处理PPT.李竹版》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378569.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

03-图像特效

一、灰度处理 方法一&#xff1a;imread方法 彩色图的颜色通道为3&#xff0c;即RGB&#xff1b;而灰度图只有一个颜色通道。 import cv2 img0 cv2.imread(E:\Jupyter_workspace\study\data/cat.png,0) img1 cv2.imread(E:\Jupyter_workspace\study\data/cat.png,1) print…

解析linux根文件系统的挂载过程

------------------------------------------ 本文系本站原创,欢迎转载!转载请注明出处:http://ericxiao.cublog.cn/------------------------------------------ 一&#xff1a;前言前段时间在编译kernel的时候发现rootfs挂载不上。相同的root选项设置旧版的image却可以。为了…

SIFT讲解(SIFT的特征点选取以及描述是重点)

目录SIFT是什么&#xff1f;尺度空间理论SIFT特征点提取SIFT特征点描述SIFT是什么&#xff1f; SIFT ,即尺度不变特征变换( Scale-invariant feature transform&#xff0c;SIFT) ,一种特征描述方法。具有 尺度鲁棒性 旋转鲁棒性 光照鲁棒性 SIFT本身包括了特征点筛选及特征点…

操作系统多线程实现_操作系统中的线程实现

操作系统多线程实现Each process has an address space. There is one thread of control in every traditional OS. Sometimes, it is viable to have multiple threads of control in the similar address space which is running in quasi-parallel. Though they were separ…

04-图像的形状绘制

一、线段绘制 cv2.line(dst,(100,100),(400,400),(0,0,255),2,cv2.LINE_AA) 参数一&#xff1a;目标图片数据 参数二&#xff1a;当前线段绘制的起始位置&#xff08;也就是两点确定一条直线&#xff09; 参数三&#xff1a;当前线段绘制的终止位置&#xff08;也就是两点确定…

(1-e^(-j5w))/(1-e^(-jw))=e^(-j2w)*sin(5w/2)/sin(w/2)的证明过程

问题出现&#xff1a;《数字信号处理第三版》第90页刘顺兰版 最后一步怎么得到的&#xff1f; 思路&#xff1a;观察答案&#xff0c;有一个自然对数项。关键就是如何提取出这一项。 我的证明过程如下&#xff1a; 参考链接&#xff1a; 【和差化积】

05-图像的美化

一、彩色图片直方图 cv2.calcHist([image],[0],None,[256],[0.0,255.0]) 该方法的所有参数都必须用中括号括起来&#xff01;&#xff01;&#xff01; 参数一&#xff1a;传入的图片数据 参数二&#xff1a;用于计算直方图的通道&#xff0c;这里使用的是灰度直方图&#xff…

Eclipse for android 中设置java和xml代码提示功能(转)

1、设置 java 文件的代码提示功能 打开 Eclipse 依次选择 Window > Preferences > Java > Editor - Content Assist > Auto activation triggers for Java &#xff0c;设置框中默认是一个点&#xff0c; 现在将它改为&#xff1a; 以下为引用内容&#xff1a; .a…

如何利用FFT(基2时间以及基2频率)信号流图求序列的DFT

直接用两个例子作为模板说明&#xff1a; 利用基2时间抽取的FFT流图计算序列的DFT 1、按照序列x[k]序号的偶奇分解为x[k]和x2[k]&#xff0c;即x1[k]{1,1,2,1}, x2[k]{-1,-1,1,2} 2、画出信号流图并同时进行计算 计算的时候需要参考基本蝶形单元&#xff1a; 关键在于 (WN) k…

matlab4.0,matlab 4.0

4.1fort-9:0.5:9if(t>0)y-(3*t^2)5;fprintf(y%.2ft%.2f\n,y,t);elsey(3*t^2)5;fprintf(y%.2ft%.2f\n,y,t);endend编译结果&#xff1a;y248.00t-9.00y221.75t-8.50y197.00t-8.00y173.75t-7.50y152.00t-7.00y131.75t-6.50y113.00t-6.00y95.75t-5.50y80.00t-5.00y65.75t-4.50y…

图形学 射线相交算法_计算机图形学中的阴极射线管

图形学 射线相交算法阴极射线管 (Cathode Ray Tube) Ferdinand Barun of Strasbourg developed the cathode ray tube in the year 1897. It used as an oscilloscope to view and measure some electrical signals. But several other technologies exist and solid state mov…

Constructor总结

一个类如果没有构造那么系统为我们在背后创建一个0参数的构造&#xff0c;但是一旦我们创建了但参数的构造&#xff0c;那么默认的构造就没了。 View Code 1 using System;2 using System.Collections.Generic;3 using System.Linq;4 using System.Text;5 6 namespace Console…

Python连接MySQL及一系列相关操作

一、首先需要安装包pymysql(python3所对应) 我使用的是Anaconda全家桶&#xff0c;打开cmd&#xff0c;进入Anaconda下的Scripts文件夹下输入命令&#xff1a;pip install pymysql进行下载安装 二、我使用的编译器为Anaconda所带的Jupyter Notebook 1&#xff0c;在mysql中…

微机原理—可编程计数器/定时器8253概念详解

目录前言【1】定时处理方法1、定时的方法&#xff1a;2、定时和计数器【2】8253计数/定时器1、特点&#xff1a;2、芯片引脚以及电路&#xff1a;3、连接方式&#xff1a;4、工作原理&#xff1a;5、寄存器配置a、初始化操作&#xff08;三个通道单独初始化&#xff09;b、读出…

形参与实参在函数中的传递

#include <iostream> #include <cstring> using namespace std; void myFun(int a[]); int main() {int a[10];cout<<"aaa"<<sizeof(a)<<endl;//40 int为4&#xff0c;a为10个int&#xff0c;故为40cout<<"yy"<<…

带你走进缓存世界

我们搞程序的多多少少都了解点算法。总体来讲&#xff0c;算法是什么&#xff1f;算法就是“时间”和“空间”的互换策略。我们常常考究一个算法的时间复杂度或空间复杂度&#xff0c;如果我们有绝对足够的时间或空间&#xff0c;那么算法就不需要了&#xff0c;可惜这种条件是…

霍夫码编码(一种不等长,非前缀编码方式)

霍夫曼编码是一种不等长非前缀编码方式&#xff0c;于1951年由MIT的霍夫曼提出。 用于对一串数字/符号编码获取最短的结果&#xff0c;获取最大的压缩效率。 特点&#xff1a;不等长、非前缀 等长式编码 等长编码&#xff0c;意思是对出现的元素采用相同位数的序号进行标定&a…

JS 获取浏览器信息,给出友情提示,避免部分兼容性问题

最近在做webform,浏览器兼容是个问题,这里我收集了一些获取浏览器信息的资料,可以给一些用户使用时,提示浏览器版本过低,让升级版本用. 这样会给开发的我们,省下很多用来调试兼容性的时间和精力. 本人就是这样想的 ~  检测浏览器及版本使用 JavaScript 检测关于访问者的浏览器…

06-机器学习(Haar+Adaboost实现人脸、人眼检测)

机器学习是什么? 机器学习训练样本特征分类器&#xff0c;通过让机器学习的方式&#xff0c;来达到某种功能的过程 深度学习是什么&#xff1f; 深度学习海量的学习样本人工神经网络 机器学习需要&#xff1a;样本、特征、分类器、对训练后的数据进行预测或检验 人脸样本haar…

Opencv实战【3】——图像修复与图像锐化(darling in the franxx)

目录前言图像修复图像锐化darling in the franxx图片总结前言 前天&#xff0c;在群里看见有人发了这张表情包&#xff1a; 感觉女主有点好看&#xff0c;然后问室友是啥番剧&#xff08;darling in the franxx&#xff09;&#xff0c;然后就去补番了&#xff0c;然后从晚上…