Advertisement

Matlab 提取Hog特征方法详细解析

阅读量:

前期时间里,撰写了一篇关于MATLAB中HOG特征提取方法浅述的文章,最初仅完成对其基本原理的初步理解。近来,对HOG特征的提取方法进行了深入探讨,并利用积分直方图加快了计算速度。为以后的应用方便起见,特意将其中涉及的核心思路、具体实现方式以及遇到的问题详细记录下来。

相关资料:

此外,在博客上还有其他相关文章值得参考:例如[()这一篇文章深入探讨了相关的技术细节;同时[()则从另一个角度提供了独特的见解;此外[()这一篇文章则详细介绍了相关的应用场景

该算法的基本实现流程如下:首先将输入图像划分为多个细胞(cell),随后每个细胞又被划分为多个块(block)。对于每个块内所有的像素点方向梯度进行计算并统计得到直方图。最后将所有块对应的直方图进行融合汇总,从而得到该图像的Hog特征描述。下面详细阐述我的编程实现思路。

主程序(Main.m):

复制代码
    <span style="font-family:SimSun;font-size:18px;">close all;clear all;clc;
    
    filePath = 'E:\HOG\HogSamples\';
    fileExt = '*.bmp';
    files = dir(fullfile(filePath,fileExt));
    len = size(files,1);
    % Generate a test Image.
    % testImg = reshape((1:16:256),4,4);
    % hogFeature = ExtrHogFeature(testImg,3,3,1,180,9);
    for i = 1:len
    tic;
    disp(['----------',num2str(i),'----------'])
    srcImg = imread(strcat(filePath,files(i,1).name));
    hogFeature = ExtrHogFeature(srcImg,5,4,2,360,15);
    toc;
    end</span>

在主程序中进行如下操作:首先遍历指定文件夹中的指定后缀名文件(如*.bmp),随后将获取的图像作为窗口图像传递给ExtrHogFeature函数。该函数是我自行开发用于提取Hog特征的功能模块。

复制代码
    <span style="font-family:SimSun;font-size:18px;">function feature = ExtrHogFeature(imgFile,cellStep,blockStep,overlap,angle,binNum)
    
    if nargin < 2
    cellStep = 3;
    blockStep = 4;
    overlap = 1;
    angle = 180;
    binNum = 9;
    elseif nargin < 6
    error('Input parameters are not enough.');
    elseif overlap < 0 || overlap >= blockStep
    error('The input parameter "overlap" is wrong!');
    elseif angle ~= 180 && angle ~= 360
    error('The input parameter "angle" is only 180 or 360!');
    end
    
    [h,w,d] = size(imgFile);
    if(d ~= 1)
    grayImg = rgb2gray(imgFile);
    else
    grayImg = imgFile;
    end
    
    % Correct the width and height of imgFile.
    if w < cellStep
    cellNum_W = 1;
    else
    cellNum_W = ceil(w / cellStep);
    if cellNum_W < blockStep
        blockNum_W = 1;
    else
        blockNum_W = floor((cellNum_W - blockStep) / (blockStep - overlap)) + 1;
    end
    end
    corrW = cellStep * ((blockStep - overlap) * (blockNum_W - 1) + blockStep);
    
    if h < cellStep
    cellNum_H = 1;
    else
    cellNum_H = ceil(h / cellStep);
    if cellNum_H < blockStep
        blockNum_H = 1;
    else
        blockNum_H = floor((cellNum_H - blockStep) / (blockStep - overlap)) + 1;
    end
    end
    corrH = cellStep * ((blockStep - overlap) * (blockNum_H - 1) + blockStep);
    
    % Bilinear interpolation
    corrImg = BiliInter(grayImg,corrH,corrW);
    
    % The following contents need to be modified.
    corrImg = double(corrImg);
    
    flipImg = FlipImg(corrImg,1,1);
    Gx = zeros(corrH,corrW);
    Gy = zeros(corrH,corrW);
    range = angle / binNum;
    for i = 1:corrH
    for j = 1:corrW
        Gx(i,j) = flipImg(i,j + 1 + 1) - flipImg(i,j);
        Gy(i,j) = flipImg(i + 1 + 1,j) - flipImg(i,j);
        Gxy(i,j) = sqrt((Gx(i,j))^2 + (Gy(i,j))^2);
    end
    end
    if angle == 180
    Axy = (atan(Gy ./ Gx) / pi) * 180;
    Axy(find(Axy < 0)) = Axy(find(Axy < 0)) + 180;
    elseif angle == 360
    Axy = (atan2(Gx,Gy) / pi) * 180;
    Axy(find(Axy < 0)) = Axy(find(Axy < 0)) + 360;
    end
    Axy(isnan(Axy)) = 0;
    Axy = floor((Axy / range) + 1);
    
    coluHist = zeros(1,corrW,binNum);
    inteHist = zeros(corrH,corrW,binNum);
    for i = 1:corrW
    coluHist(1,i,Axy(1,i)) = Gxy(1,i);
    inteHist(1,i,Axy(1,i)) = Gxy(1,i);
    if i > 1
        inteHist(1,i,:) = inteHist(1,i,:) + inteHist(1,i - 1,:);
    end
    end
    for i = 2:corrH
    for j = 1:corrW
    	coluHist(1,j,Axy(i,j)) = coluHist(1,j,Axy(i,j)) + Gxy(i,j);
        if j == 1          
            inteHist(i,j,:) = coluHist(1,j,:);
        else          
            inteHist(i,j,:) = inteHist(i,j - 1,:) + coluHist(1,j,:);
        end
    end
    end
    
    tmpFeature = zeros(1,binNum);
    for i = 1:blockNum_H
    for j = 1:blockNum_W
        i1 = (i * (blockStep - overlap) + overlap - blockStep + 1) * cellStep;
        j1 = (j * (blockStep - overlap) + overlap - blockStep + 1) * cellStep;
        i2 = (i * (blockStep - overlap) + overlap - blockStep + 1) * cellStep;
        j2 = (j * (blockStep - overlap) + overlap) * cellStep;
        i3 = (i * (blockStep - overlap) + overlap) * cellStep;
        j3 = (j * (blockStep - overlap) + overlap - blockStep + 1) * cellStep;
        i4 = (i * (blockStep - overlap) + overlap) * cellStep;
        j4 = (j * (blockStep - overlap) + overlap) * cellStep;
        if i == 1 && j == 1
            tmpFeature(:) = inteHist(i4,j4,:);
            feature = tmpFeature;
        else
            tmpFeature(:) = inteHist(i4,j4,:) - inteHist(i2,j2,:) - inteHist(i3,j3,:) + inteHist(i1,j1,:);
            feature = [feature,tmpFeature];
        end
    end
    end
    
    maxValue = max(feature(:));
    minValue = min(feature(:));
    feature = (feature - minValue) / (maxValue - minValue) * 1000;
    % save ExtrHogFeature_Data</span>

在ExtrHogFeature函数中:

1.参数意义:

(1).imgFile:输入的窗口图像文件。

(2). cellStep表示每个单元格包含多少个像素。理论上单元格宽度与高度可以不一致。然而,在编程实现中并无实际价值。因此我们默认每个单元格为正方形。同样的逻辑适用于blockStep和overlap

(3).blockStep:block的步长,即一个block有多少个cell。注意:不是有多少个像素,是有多少个cell。

(4).overlap:重叠数目。即后(下)一个block与前(上)一个block有几列(行)cell重叠。

(5).angle:只能是两个值,180或者360。

(6).binNum:积分直方图的区域数量。

2.判断输入参数是否正确。

(1).判断参数的数量。参数的数量只能是两种,1个或者6个。如果是1个,对除imgFile之外的其他参数进行默认赋值。

(2).评估overlap是否合理。其中overlap变量代表两个block之间的重叠数量,因此, overlap值必须严格小于blockStep.

(3).判断angle是否正确。angle只能是180或360。

识别图像的空间维度后并将其转换为灰度图。该过程涉及将图像标准化至Gamma和Color空间。从数学理论层面而言,则仅需对方阵根数进行计算。为了提高计算效率而采用灰度处理

在提取图像特征时, 我始终坚持"避免减少图像特征"的原则. 即使面对原始图像难以直接生成整数倍cell和block的情况, 我们仍然会选择通过双线性插值方法对原始图像进行缩放处理, 从而能够均匀地划分成整数数量的block.

该函数对testImg进行了处理操作。其中testImg为大小均为20像素的测试窗口图像。其中cellStep被设定为3;而每行cell的数量计算为20除以3得到6.6;因此取其整数部分并向上取整得到7个单元格数量即为cellNum_W的值。

设blockNum_W是每一行block的数量,满足下列公式:

(blockStep - overlap) * (blockNum_W - 1) + blockStep >= cellNum_W ①

取满足公式的最小整数。即:

blockNum_W = floor((cellNum_W - blockStep) / (blockStep - overlap)) + 1 ②

基于公式①的计算结果表明, Block number W达到至少2.5, 因此(同样地,也可以通过公式②得出)Block number W被确定为3. 计算Cell数量W时, 按照如下公式进行: (4−2)×(3−1)+4 = 8. 经过校正后得到的图像宽度corrw被确定为8乘以3等于24个像素单位. 高度corrh的计算方式与此类似

基于corrW和corrH的方法用于对原始图像实施双线性插值缩放。双线性插值函数BiliInter位于本文末尾部分。由于此内容属于文章次要部分且较为简单明了,故无需进一步阐述。

5.计算每个像素的方向梯度。水平模板fx=[-1,0,1],垂直模板fy=fx’。

为了实现corrImg四个边界的方向梯度计算过程,请将这四个边界进行反转操作。与双线性插值方法类似,在本文的末尾处提供了反转图像函数FlipImg的实现细节。

注意:atan和atan2的区别。atan(y/x)根据正切值y/x求出对应的角度,是2象限的反正切。atan2(y,x)的取值不仅取决于y/x的正切值,还取决于(x,y)落于哪个象限,是4象限的反正切。所以,当angle=180的时候,经过atan计算小于0的值是在-pi/20之间,需要+pi,把反正切值校正到pi/2pi。当angle=360的时候,经过atan2计算小于0的值是在-pi0之间,需要+2pi,把反正切值校正到pi2pi。

Axy中可能会出现0/0=Nan的情况,造成程序中止。所以,要有Axy(isnan(Axy)) = 0。

6.为了获取原始窗口图像对应的积分直方图inteHist值,则需要调用算法实现该计算功能。在具体实现过程中,我是参考文献2的基础上进行代码开发,并在此基础上对二维积分图计算方法进行了适当的拓展与优化处理,在此基础上实现了三维积分直方图的具体计算过程

基于积分直方图的方法,在处理窗口图像时,首先通过计算每个块在窗口中的积分直方图,并将这些块的积分直方图进行综合处理,从而得到图像的整体HOG特征描述。接着对这些特征值进行归一化处理至0到1000的比例范围内。

BiliInter函数:

复制代码
    <span style="font-family:SimSun;font-size:18px;">function img = BiliInter(imgFile,dstH,dstW)
    
    if dstH <= 0 || dstW <= 0
    error('Input parameters are wrong!');
    end
    
    [srcH,srcW] = size(imgFile);
    img = zeros(dstH,dstW);
    for i = 0:dstH - 1
    for j = 0:dstW - 1
        actualW = (j / (dstW - 1)) * (srcW - 1);
        actualH = (i / (dstH - 1)) * (srcH - 1);
        w_Scale = actualW - floor(actualW);
        h_Scale = actualH - floor(actualH);
        leftUp = [floor(actualH) + 1,floor(actualW) + 1];
        rightUp = [floor(actualH) + 1,ceil(actualW) + 1];
        leftLow = [ceil(actualH) + 1,floor(actualW) + 1];
        rightLow = [ceil(actualH) + 1,ceil(actualW) + 1];
        tmpUp = (imgFile(rightUp(1),rightUp(2)) - imgFile(leftUp(1),leftUp(2))) * w_Scale + imgFile(leftUp(1),leftUp(2));
        tmpLow = (imgFile(rightLow(1),rightLow(2)) - imgFile(leftLow(1),leftLow(2))) * w_Scale + imgFile(leftLow(1),leftLow(2));
        img(i + 1,j + 1) = (tmpLow - tmpUp) * h_Scale + tmpUp;
    end
    end
    img = uint8(img);</span>

FlipImg函数:

复制代码
    <span style="font-family:SimSun;font-size:18px;">function img = FlipImg(imgFile,flipRowNum,flipColNum)
    % The flipRowNum is added on imgFile one way.
    % The flipColNum is similar to flipRowNum.
    [srcH,srcW] = size(imgFile);
    if flipRowNum > srcH || flipColNum > srcW
    error('Input parameters are too big!');
    end
    
    dstH = srcH + 2 * flipRowNum;
    dstW = srcW + 2 * flipColNum;
    tmpImgRow = zeros(dstH,srcW);
    img = zeros(dstH,dstW);
    % Fliped with Row.
    tmpImgRow(1:flipRowNum,:) = imgFile(flipRowNum:-1:1,:);
    tmpImgRow(flipRowNum + 1:flipRowNum + srcH,:) = imgFile(:,:);
    tmpImgRow(flipRowNum + srcH + 1:end,:) = imgFile(srcH:-1:srcH - flipRowNum + 1,:);
    % Fliped with Column.
    img(:,1:flipColNum) = tmpImgRow(:,flipColNum:-1:1);
    img(:,flipColNum + 1:flipColNum + srcW) = tmpImgRow(:,:);
    img(:,flipColNum + srcW + 1:end) = tmpImgRow(:,srcW:-1:srcW - flipColNum + 1);</span>

全部评论 (0)

还没有任何评论哟~