hog特征原理详解及matlab代码学习笔记
1、HOG特征:
⽅向梯度直⽅图(Histogram of Oriented Gradient, HOG)特征是⼀种在计算机视觉和图像处理中⽤来进⾏物体检测的特征描述⼦。它通过计算和统计图像局部区域的梯度⽅向直⽅图来构成特征。Hog特征结合SVM分类器已经被⼴泛应⽤于图像识别中,尤其在⾏⼈检测中获得了极⼤的成功。需要提醒的是,HOG+SVM进⾏⾏⼈检测的⽅法是法国研究⼈员Dalal在2005的CVPR上提出的,⽽如今虽然有很多⾏⼈检测算法不断提出,但基本都是以HOG+SVM的思路为主。
(1)主要思想:
在⼀副图像中,局部⽬标的表象和形状(appearance and shape)能够被梯度或边缘的⽅向密度分布很好地描述。(本质:梯度的统计信息,⽽梯度主要存在于边缘的地⽅)。
(2)具体的实现⽅法是:
⾸先将图像分成⼩的连通区域,我们把它叫细胞单元。然后采集细胞单元中各像素点的梯度的或边缘的⽅向直⽅图。最后把这些直⽅图组合起来就可以构成特征描述器。
(3)提⾼性能:
把这些局部直⽅图在图像的更⼤的范围内(我们把它叫区间或block)进⾏对⽐度归⼀化(contrast-normalized),所采⽤的⽅法是:先计算各直⽅图在这个区间(block)中的密度,然后根据这个密度对区间中的各个细胞单元做归⼀化。通过这个归⼀化后,能对光照变化和阴影获得更好的效果。
(4)优点:
与其他的特征描述⽅法相⽐,HOG有很多优点。⾸先,由于HOG是在图像的局部⽅格单元上操作,所以它对图像⼏何的和光学的形变都能保持很好的不变性,这两种形变只会出现在更⼤的空间领域上。其次,在粗的空域抽样、精细的⽅向抽样以及较强的局部光学归⼀化等条件下,只要⾏⼈⼤体上能够保持直⽴的姿势,可以容许⾏⼈有⼀些细微的肢体动作,这些细微的动作可以被忽略⽽不影响检测效果。因
此HOG特征是特别适合于做图像中的⼈体检测的。
2、HOG特征提取算法的实现过程:
⼤概过程:
HOG特征提取⽅法就是将⼀个image(你要检测的⽬标或者扫描窗⼝):
1)灰度化(将图像看做⼀个x,y,z(灰度)的三维图像);
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了。这个就是最终的可供分类使⽤的特征向量了。
具体每⼀步的详细过程如下:
(1)标准化gamma空间和颜⾊空间
为了减少光照因素的影响,⾸先需要将整个图像进⾏规范化(归⼀化)。在图像的纹理强度中,局部的表层曝光贡献的⽐重较⼤,所以,这种压缩处理能够有效地降低图像局部的阴影和光照变化。因为颜⾊信息作⽤不⼤,通常先转化为灰度图;
Gamma压缩公式:
⽐如可以取Gamma=1/2;
(2)计算图像梯度
计算图像横坐标和纵坐标⽅向的梯度,并据此计算每个像素位置的梯度⽅向值;求导操作不仅能够捕获轮廓,⼈影和⼀些纹理信息,还能进⼀步弱化光照的影响。
图像中像素点(x,y)的梯度为:
最常⽤的⽅法是:⾸先⽤[-1,0,1]梯度算⼦对原图像做卷积运算,得到x⽅向(⽔平⽅向,以向右为正⽅向)的梯度分量gradscalx,然后⽤[1,0,-1]T梯度算⼦对原图像做卷积运算,得到y⽅向(竖直⽅向,以向上为正⽅向)的梯度分量gradscaly。然后再⽤以上公式计算该像素点的梯度⼤⼩和⽅向。
(3)为每个细胞单元构建梯度⽅向直⽅图
第三步的⽬的是为局部图像区域提供⼀个编码,同时能够保持对图像中⼈体对象的姿势和外观的弱敏感性。
我们将图像分成若⼲个“单元格cell”,例如每个cell为6*6个像素。假设我们采⽤9个bin的直⽅图来统计这6*6个像素的梯度信息。也就是
将cell的梯度⽅向360度分成9个⽅向块,如图所⽰:例如:如果这个像素的梯度⽅向是20-40度,直⽅图第2个bin的计数就加⼀,这样,
对cell内每个像素⽤梯度⽅向在直⽅图中进⾏加权投影(映射到固定的⾓度范围),就可以得到这个cell的梯度⽅向直⽅图了,就是该cell对应的9维特征向量(因为有9个bin)。
像素梯度⽅向⽤到了,那么梯度⼤⼩呢?梯度⼤⼩就是作为投影的权值的。例如说:这个像素的梯度⽅向是20-40度,然后它的梯度⼤⼩是2(假设啊),那么直⽅图第2个bin的计数就不是加⼀了,⽽是加⼆(假设啊)。
细胞单元可以是矩形的(rectangular),也可以是星形的(radial)。
(4)把细胞单元组合成⼤的块(block),块内归⼀化梯度直⽅图
由于局部光照的变化以及前景-背景对⽐度的变化,使得梯度强度的变化范围⾮常⼤。这就需要对梯度强度做归⼀化。归⼀化能够进⼀步地对光照、阴影和边缘进⾏压缩。
作者采取的办法是:把各个细胞单元组合成⼤的、空间上连通的区间(blocks)。这样,⼀个block内所有cell的特征向量串联起来便得到该block的HOG特征。这些区间是互有重叠的,这就意味着:每⼀个单元格的特征会以不同的结果多次出现在最后的特征向量中。我们将归⼀化之后的块描述符(向量)就称之为HOG描述符。
区间有两个主要的⼏何形状——矩形区间(R-HOG)和环形区间(C-HOG)。R-HOG区间⼤体上是⼀些⽅形的格⼦,它可以有三个参数来表征:每个区间中细胞单元的数⽬、每个细胞单元中像素点的数⽬、每个细胞的直⽅图通道数⽬。
例如:⾏⼈检测的最佳参数设置是:3×3细胞/区间、6×6像素/细胞、9个直⽅图通道。则⼀块的特征数为:3*3*9;
(5)收集HOG特征
最后⼀步就是将检测窗⼝中所有重叠的块进⾏HOG特征的收集,并将它们结合成最终的特征向量供分类使⽤。
(6)那么⼀个图像的HOG特征维数是多少呢?
顺便做个总结:Dalal提出的Hog特征提取的过程:把样本图像分割为若⼲个像素的单元(cell),把梯度⽅向平均划分为9个区间(bin),在每个单元⾥⾯对所有像素的梯度⽅向在各个⽅向区间进⾏直⽅图统计,得到⼀个9维的特征向量,每相邻的4个单元构成⼀个块(block),把⼀个块内的特征向量联起来得到36维的特征向量,⽤块对样本图像进⾏扫描,扫描步长为⼀个单元。最后将所有块的特征串联起来,就得到了⼈体的特征。例如,对于64*128的图像⽽⾔,每8*8的像素组成⼀个cell,每2*2个cell组成⼀个块,因为每个cell有9个特征,所以每个块内有4*9=36个特征,以8个像素为步长,那么,⽔平⽅向将有7个扫描窗⼝,垂直⽅向将有15个扫描窗⼝。也就是
说,64*128的图⽚,总共有36*7*15=3780个特征。
HOG维数,16×16像素组成的block,8x8像素的cell
注释:
⾏⼈检测HOG+SVM
总体思路:
1、提取正负样本hog特征
2、投⼊svm分类器训练,得到model
3、由model⽣成检测⼦
4、利⽤检测⼦检测负样本,得到hardexample
5、提取hardexample的hog特征并结合第⼀步中的特征⼀起投⼊训练,得到最终检测⼦。
1.对原图像gamma校正,img=sqrt(img);
2.求图像竖直边缘,⽔平边缘,边缘强度,边缘斜率。
3.将图像每16*16(取其他也可以)个像素分到⼀个cell中。对于256*256的lena来说,就分成了16*16个cell了。
4.对于每个cell求其梯度⽅向直⽅图。通常取9(取其他也可以)个⽅向(特征),也就是每360/9=40度分到⼀个⽅向,⽅向⼤⼩按像素边缘强度加权。
5.每2*2(取其他也可以)个cell合成⼀个block,所以这⾥就有(16-1)*(16-1)=225个block。最后归⼀化直⽅图。
6.所以每个block中都有2*2*9个特征,⼀共有225个block,所以总的特征有225*36个。
当然⼀般HOG特征都不是对整幅图像取的,⽽是对图像中的⼀个滑动窗⼝取的。
lena图:
求得的225*36个特征:
matlab代码如下:
clear all; close all; clc;
img=double(imread('lena.jpg'));
imshow(img,[]);
[m n]=size(img);
img=sqrt(img);      %伽马校正
matlab直方图
%下⾯是求边缘
fy=[-1 0 1];        %定义竖直模板
fx=fy';            %定义⽔平模板
Iy=imfilter(img,fy,'replicate');    %竖直边缘
Ix=imfilter(img,fx,'replicate');    %⽔平边缘
Ied=sqrt(Ix.^2+Iy.^2);              %边缘强度
Iphase=Iy./Ix;              %边缘斜率,有些为inf,-inf,nan,其中nan需要再处理⼀下
%下⾯是求cell
step=16;                %step*step个像素作为⼀个单元
orient=9;              %⽅向直⽅图的⽅向个数
jiao=360/orient;        %每个⽅向包含的⾓度数
Cell=cell(1,1);              %所有的⾓度直⽅图,cell是可以动态增加的,所以先设了⼀个ii=1;
jj=1;
for i=1:step:m          %如果处理的m/step不是整数,最好是i=1:step:m-step
ii=1;
for j=1:step:n      %注释同上
tmpx=Ix(i:i+step-1,j:j+step-1);
tmped=Ied(i:i+step-1,j:j+step-1);
tmped=tmped/sum(sum(tmped));        %局部边缘强度归⼀化
tmpphase=Iphase(i:i+step-1,j:j+step-1);
Hist=zeros(1,orient);              %当前step*step像素块统计⾓度直⽅图,就是cell        for p=1:step
for q=1:step
if isnan(tmpphase(p,q))==1  %0/0会得到nan,如果像素是nan,重设为0
if isnan(tmpphase(p,q))==1  %0/0会得到nan,如果像素是nan,重设为0
tmpphase(p,q)=0;
end
ang=atan(tmpphase(p,q));    %atan求的是[-90 90]度之间
ang=mod(ang*180/pi,360);    %全部变正,-90变270
if tmpx(p,q)<0              %根据x⽅向确定真正的⾓度
if ang<90              %如果是第⼀象限
ang=ang+180;        %移到第三象限
end
if ang>270              %如果是第四象限
ang=ang-180;        %移到第⼆象限
end
end
ang=ang+0.0000001;          %防⽌ang为0
Hist(ceil(ang/jiao))=Hist(ceil(ang/jiao))+tmped(p,q);  %ceil向上取整,使⽤边缘强度加权
end
end
%Hist=Hist/sum(Hist);    %⽅向直⽅图归⼀化,这⼀步可以没有,因为是组成block以后再进⾏归⼀化就可以        Cell{ii,jj}=Hist;      %放⼊Cell中
ii=ii+1;                %针对Cell的y坐标循环变量
end
jj=jj+1;                    %针对Cell的x坐标循环变量
end
%下⾯是求feature,2*2个cell合成⼀个block,没有显式的求block
[m n]=size(Cell);
feature=cell(1,(m-1)*(n-1));
for i=1:m-1
for j=1:n-1
f=[];
f=[f Cell{i,j}(:)' Cell{i,j+1}(:)' Cell{i+1,j}(:)' Cell{i+1,j+1}(:)'];
f=f./sum(f);%归⼀化
feature{(i-1)*(n-1)+j}=f;
end
end
%到此结束,feature即为所求
%下⾯是为了显⽰⽽写的
l=length(feature);
f=[];
for i=1:l
f=[f;feature{i}(:)'];
end
figure
mesh(f)