HOG算法

目录

HOG算法思想:

颜色空间归一化:

梯度计算:

计算细胞单元的梯度直方图:

对组合成块的梯度直方图作归一化:

对归一化梯度特征进行SVM分类:


https://blog.csdn.net/yongjiankuang/article/details/79808346

https://www.cnblogs.com/codingmengmeng/p/5613442.html

HOG算法思想:

HOG的发明者是Navneet Dalal,在2005年其在CVPR上发表了Histograms of Oriented Gradients for Human Detection。ND是Inria数据集的缔造者,其博士的毕业论文Finding People in Images and Videos是HOG研究者的一手资料。

在计算机视觉以及数字图像处理中梯度方向直方图(HOG)是一种能对物体进行检测的基于形状边缘特征的描述算子,它的基本思想是利用梯度信息能很好的反映图像目标的边缘信息并通过局部梯度的大小将图像局部的外观和形状特征化。一些研究者利用梯度HOG特征并结合其他特征对人体进行检测得到了较好的结果。

HOG特征的提取可以用下图所示的过程表示: 颜色空间的归一化是为了减少光照以及背景等因素的影响;划分检测窗口成大小相同的细胞单元(cell),并分别提取相应的梯度信息;组合相邻的细胞单元成大的相互有重叠的块(block),这样能有效的利用重叠的边缘信息,以统计整个块的直方图;并对每个块内的梯度直方图进行归一化,从而进一步减少背景颜色及噪声的影响;最后将整个窗口中所有块的HOG特征收集起来,并使用特征向量来表示其特征。

在这一过程中,不同尺度的参数模板、梯度方向的选择、重叠块及单元格的大小还有归一化因子等因素都会影响最终的检测结果。最终通过SVM分类器分离出正确的行人目标。

 HOG的整体流程图如下所示:

1、  将一个image(你要检测的目标或者扫描窗口)灰度化的三维图像;(因为颜色信息作用不大,通常先转化为灰度图

2、  采用Gamma校正法对输入图像进行颜色空间的标准化(归一化);目的是调节图像的对比度,降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪音的干扰;

3、  计算图像每个像素的梯度(包括大小和方向);主要是为了捕获轮廓信息,同时进一步弱化光照的干扰。

4、  将图像划分成小cells(例如6*6像素/cell);

5、  统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor;

6、  将每几个cell组成一个block(例如3*3个cell/block),一个block内所有cell的特征descriptor串联起来便得到该block的HOG特征descriptor。

7、  将图像image内的所有block的HOG特征descriptor串联起来就可以得到该image(你要检测的目标)的HOG特征descriptor了。这个就是最终的可供分类使用的特征向量了。

8、  整个过程如下图

颜色空间归一化:

在现实的情况,图像目标会出现在不同的环境中,光照也会有所不一样,颜色空间归一化就是对整幅图像的颜色信息作归一化处理从而减少不同光照及背景的影响,也为了提高检测的鲁棒性,引入图像Gamma和颜色空间归一化来作为特征提取的预处理手段。ND大神等人也对不同的图像像素点的表达方式包括灰度空间等进行了评估,最终验证RGB还有LAB色彩空间能使检测结果大致相同且能起到积极的影响,且另一方面,ND大神等人在研究中分别在每个颜色通道上使用了两种不同的Gamma归一化方式,取平方根或者使用对数法,最终验证这一预处理对检测的结果几乎没有影响。而且不能对图像进行高斯平滑处理,因平滑处理会降低图像目标边缘信息的辨识度,影响检测结果。

梯度计算:

边缘是由图像局部特征包括灰度、颜色和纹理的突变导致的。一幅图像中相邻的像素点之间变化比较少,区域变化比较平坦,则梯度幅值就会比较小,反之,则梯度幅值就会比较大。梯度在图像中对应的就是其一阶导数。模拟图像f(x,y)中任一像素点(x,y)的梯度是一个矢量:

 

其中,Gx是沿x方向上的梯度,Gy是沿y方向上的梯度,梯度的幅值及方向角可表示如下:

数字图像中像素点的梯度是用差分来计算的:

一维离散微分模板在将图像的梯度信息简单、快速且有效地计算出来,其公式如下:

式中,Gx,Gy,H(x,y)分别表示的是像素点(x,y)在水平方向上及垂直方向上的梯度以及像素的灰度值,其梯度的幅值及方向计算公式如下:

ND大神等人也验证,不同的梯度运算模板在其检测效果上也不一样,如下表,可以看出,使用简单的一维离散微分模板[-1,0,1]进行的梯度运算得到的检测效果是最好的,而使用其他形式的梯度运算模板如Prewitt和Sobel等算子,如下图所示,不仅增加运算量而同时也降低了其检测效果。

 

计算细胞单元的梯度直方图:

对于整个目标窗口,我们需要将其分成互不重叠大小相同的细胞单元(cell),然后分别计算出每个cell的梯度信息,包括梯度大小和梯度方向。ND大神等人实验指出,将像素的梯度方向在0-180°区间内平均划分为9个bins,超过9个时不仅检测性能没有明显的提高反而增加了检测运算量, 每个cell内的像素为其所在的梯度方向直方图进行加权投票,加权的权值可以是像素本身的梯度幅值,也可以是幅值的平方或平方根等,而若使用平方或平方根,实验的检测性能会有所降低,ND大神等人也验证,使用梯度幅值的实验效果更可靠。

直方图是有9个bin的向量,代表的是角度0,20,40,60.....160。梯度方向矩阵中可以看到角度是0-180度,不是0-360度,这种被称之为"无符号"梯度("unsigned" gradients)因为一个梯度和它的负数是用同一个数字表示的,也就是说一个梯度的箭头以及它旋转180度之后的箭头方向被认为是一样的。那为什么不用0-360度的表示呢?在事件中发现unsigned gradients比signed gradients在行人检测任务中效果更好。一些HOG的实现中可以让你指定signed gradients。

                                                                                    梯度直方图的计算

这里有个细节要注意,如果一个角度大于160度,也就是在160-180度之间,我们知道这里角度0,180度是一样的,所以在下面这个例子里,像素的角度为165度的时候,要把幅值按照比例放到0和160的bin里面去。

把这8*8的cell里面所有的像素点都分别加到这9个bin里面去,就构建了一个9-bin的直方图,上面的网格对应的直方图如下:

这里,在我们的表示中,Y轴是0度(从上往下)。你可以看到有很多值分布在0,180的bin里面,这其实也就是说明这个网格中的梯度方向很多都是要么朝上,要么朝下。

对组合成块的梯度直方图作归一化:

从梯度计算公式中可以看出,梯度幅值绝对值的大小容易受到前景与背景对比度及局部光照的影响,要减少这种影响得到较准确的检测效果就必须对局部细胞单元进行归一化处理。归一化方法多种多样,但整体思想基本上是一致的:将几个细胞单元(cell)组合成更大的块(block),这时整幅图像就可看成是待检测窗口,将更大的块看成是滑动窗口,依次从左到右从上到下进行滑动,得到一些有重复细胞单元的块及一些相同细胞单元(cell)在不同块(block)中的梯度信息,再对这些块(block)信息分别作归一化处理,不同的细胞单元尺寸大小及不同块的尺寸大小会影响最终的检测效果。

假设64×64像素是检测的窗口的尺寸,分成 4×4=16 个细胞单元(cell),如下图中黑色的小框(1-16),16×16 像素是每个细胞单元的大小,块(block)是由相邻的 2×2=4 个细胞单元组成的,如图紫色的小框,滑动窗口的大小为一个块的大小,依次将滑动窗口从左到右从上到下进行滑动来获得整个待测窗口的边缘信息,得到9个块,统计这9个块在9个不同方向上的梯度信息,在整个窗口中得到的梯度特征是9×9=81 维的向量。在实际情况中我们的检测窗口的大小为128×64像素,一个细胞单元的大小为8×8像素,由2×2个细胞单元组成大小为16×16像素的块,一个细胞单元的梯度直方图化成9个bins块的移动步长是8个像素,则检测窗口在图像中移动的步长也为8个像素,这样检测窗口就有((128-16)/8+1)×((64-16)/8+1)= 105个块,一个块有4个细胞单元,每个细胞单元的HOG特征向量长度是9,则最终的HOG特征描述符大小就是105×4×9 = 3780维。

检测窗口 —> 滑动窗口(一个块的大小1)—>细胞单元—>像素点

对于块的梯度直方图向量的归一化,ND大神等人使用了不同方法,并对结果进行了比较,假设V是未归一化的向量,是一个很小的必要的常数,下式是定义的范数的函数表达式:

L1范数:

L2范数:

归一化计算公式为:

L2常规:

L1平方根:

ND大神等人指出,用L1常规方法进行向量归一化比用L2常规和L1平方根方法而得到的检测效果降低5%。

通常HOG特征描述子是画出8*8网格中9*1归一化的直方图,见下图。你可以发现直方图的主要方向捕捉了这个人的外形,特别是躯干和腿。

对归一化梯度特征进行SVM分类:

如下图所示,对于滑动窗口提取的2个窗口,分别计算出归一化的梯度特征,然后应用SVM实现是人还是背景的分类判定。

 

程序实现:

python:

# -*- coding: utf-8 -*-
"""
Created on Sun Dec 23 20:54:43 2018

@author: Administrator
"""

# -*- coding: utf-8 -*-
"""
Created on Mon Sep 24 16:43:37 2018

@author: zy
"""

'''
HOG检测人
'''
import  cv2
import numpy as np

def is_inside(o,i):
    '''
    判断矩形o是不是在i矩形中
    
    args:
        o:矩形o  (x,y,w,h)
        i:矩形i  (x,y,w,h)
    '''
    ox,oy,ow,oh = o
    ix,iy,iw,ih = i
    return ox > ix and oy > iy and ox+ow < ix+iw and oy+oh < iy+ih
    
def draw_person(img,person):
    '''
    在img图像上绘制矩形框person
    
    args:
        img:图像img
        person:人所在的边框位置 (x,y,w,h)
    '''
    x,y,w,h = person
    cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,255),2)
    

def detect_test():
    '''
    检测人
    '''
    img = cv2.imread('traffic2.jpg')
    rows,cols = img.shape[:2]
    sacle = 1.0
    #print('img',img.shape)
    img = cv2.resize(img,dsize=(int(cols*sacle),int(rows*sacle)))
    #print('img',img.shape)
    
    #创建HOG描述符对象
    #计算一个检测窗口特征向量维度:(64/8 - 1)*(128/8 - 1)*4*9 = 3780
    '''
    winSize = (64,128)
    blockSize = (16,16)    
    blockStride = (8,8)
    cellSize = (8,8)
    nbins = 9    
    hog = cv2.HOGDescriptor(winSize,blockSize,blockStride,cellSize,nbins)  
    '''
    hog = cv2.HOGDescriptor()  
    #hist = hog.compute(img[0:128,0:64])   计算一个检测窗口的维度
    #print(hist.shape)
    detector = cv2.HOGDescriptor_getDefaultPeopleDetector()
    print('detector',type(detector),detector.shape)    
    hog.setSVMDetector(detector)

    
    
    #多尺度检测,found是一个数组,每一个元素都是对应一个矩形,即检测到的目标框
    found,w = hog.detectMultiScale(img)
    print('found',type(found),found.shape)
    
    #过滤一些矩形,如果矩形o在矩形i中,则过滤掉o
    found_filtered = []
    for ri,r in enumerate(found):
        for qi,q in enumerate(found):
            #r在q内?
            if ri != qi and is_inside(r,q):
                break
        else:
            found_filtered.append(r)
            
    for person in found_filtered:
        draw_person(img,person)
        
    cv2.imshow('img',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
if __name__=='__main__':
    detect_test()

c++:

opencv中包含了2种HOG的实现途径,一种是HOG+SVM的实现方法,另一种是HOG+Cascade的实现方法。两种方法各有千秋,具体实现程序如下所示。

#include <iostream>  
#include <string>  
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
#include <opencv2/objdetect/objdetect.hpp>  
#include <opencv2/ml/ml.hpp>  
#include<ctime>  
  
using namespace std;  
using namespace cv;  
  
int main()  
{  
    cout<<"Red:Hog+svm------Green:Hog+cascade"<<endl;  
    Mat src = imread("1.jpg",1);  
    vector<Rect> found1, found_filtered1,found2, found_filtered2;//矩形框数组  
  
    clock_t start1,end1,start2,end2;  
    //方法1,Hog+svm  
    start1 = clock();  
    HOGDescriptor hog;//HOG特征检测器  
    hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());//设置SVM分类器为默认参数     
    hog.detectMultiScale(src, found1, 0, Size(2,2), Size(0,0), 1.05, 2);//对图像进行多尺度检测,检测窗口移动步长为(8,8)  
    end1=(double)(1000*(clock()-start1)/CLOCKS_PER_SEC);  
    //方法2.Hog+cascade  
    start2 = clock();  
    CascadeClassifier *cascade = new CascadeClassifier;  
    cascade->load("hogcascade_pedestrians.xml");  
    cascade->detectMultiScale(src, found2);  
    end2=(double)(1000*(clock()-start2)/CLOCKS_PER_SEC) ;  
  
    cout<<"Hog+svm:  "<<end1<<"ms"<<"    Hog+cascade:  "<<end2<<"ms"<<endl;  
    //找出所有没有嵌套的矩形框r,并放入found_filtered中,如果有嵌套的话,则取外面最大的那个矩形框放入found_filtered中  
    for(int i=0; i < found1.size(); i++)  
    {  
        Rect r = found1[i];  
        int j=0;  
        for(; j < found1.size(); j++)  
            if(j != i && (r & found1[j]) == r)  
                break;  
        if( j == found1.size())  
            found_filtered1.push_back(r);  
    }  
    for(int i=0; i < found2.size(); i++)  
    {  
        Rect r = found2[i];  
        int j=0;  
        for(; j < found2.size(); j++)  
            if(j != i && (r & found2[j]) == r)  
                break;  
        if( j == found2.size())  
            found_filtered2.push_back(r);  
    }  
  
    //画矩形框,因为hog检测出的矩形框比实际人体框要稍微大些,所以这里需要做一些调整  
    for(int i=0; i<found_filtered1.size(); i++)  
    {  
        Rect r = found_filtered1[i];  
        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);  
        rectangle(src, r.tl(), r.br(), Scalar(0,0,255), 3);  
    }  
    for(int i=0; i<found_filtered2.size(); i++)  
    {  
        Rect r = found_filtered2[i];  
        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);  
        rectangle(src, r.tl(), r.br(), Scalar(0,255,0), 3);  
    }  
    imshow("src",src);  
    waitKey();  
  
    system("pause");  
    return 0;  
}  

运行当前目录下的Shell脚本,run.sh,就会生成运行结果,程序运行效果如下图所示,其中红色的为HOG+SVM的检测效果,绿色的为HOG+Cascade的检测效果。其中,HOG+SVM的运行时间为739ms, HOG+Cascade的运行时间为30ms。

HOG算法与SIFT和LBP比较

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值