zoukankan      html  css  js  c++  java
  • OpenCV 2.4+ C++ 行人检测

    HOG特征描述

    首先我们来了解一下HOG特征描述子。

    HOG特征描述子(HOG descriptors)是由Navneet Dalal和 Bill Triggs在2005年的一篇介绍行人检测方法的论文提到的特征描述子(论文以及演讲可参见参考资料1、2)。

    其主要思想是计算局部图像梯度的方向信息的统计值,来作为该图像的局部特征值。

    • 如上图,归一化图像后,由于颜色数据对我们没有帮助,所以将图片转为灰度图。
    • 然后将图片分割成一定“块”数,称作细胞单元。
    • 计算每个细胞单元的梯度大小方向。
    • 得到每个单元的梯度方向组成一个图片的特征向量。
    • 将这个特征向量交给SVM来学习或辨认。

    SVM的简单介绍可以参考:OpenCV 2.4+ C++ SVM文字识别

    算法分析

    • 梯度计算

     OpenCV 2.4+ C++ 边缘梯度计算中,介绍了Sobel算子和Scharr滤波器的梯度计算方法,但是论文作者使用这些复杂方法的效果并不好,最好的是使用简单的一维梯度算子:[-1, 0, 1],进行卷积计算,分别算出x方向梯度Gx与y方向梯度Gy。然后利用下面两个公式计算梯度大小和方向:

        G = \sqrt{ G_{x}^{2} + G_{y}^{2} }

        

    •  平滑处理

     论文作者使用了高斯模糊进行平滑处理,结果效果变差了。其可能原因是:许多有用的图像信息是来自变化剧烈的边缘,而在计算梯度之前加入高斯滤波会把这些边缘滤除掉。

    •  细胞方向选择
    1. 细胞的统计方向,由细胞中的每一个像素点投票取得。
    2. 票箱是0 - PI(无向)角度范围平分为9个角度,即PI/9为每个票箱的角度范围。
    3. 每个投票像素点的投票权重,由其梯度幅值计算出来,可采用幅值本身(实验效果最佳),或者他的函数来表示这个权重。
    • 块 

    局部关照变化或者前景和背景的对比变化,可能使梯度强度产生剧烈变化,但我们关注的不是这些信息,所以需要对这些信息弱化处理。利用数个细胞单元组成空间上连通的块。这样,HOG描述就成了由各个块所有细胞单元的直方图组成的一个向量,这些区域是互相重叠的。这样就减小了这些剧烈变化的影响。

    每个细胞是8*8个像素点,以四个细胞组成一个块。

     再由块组成检测窗。

    • 块向量归一化

    最后是利用L2-norm或者L1-norm进行归一化。

    v是未被归一化的向量,| vk |是k阶范数,ε为任意小常数,当k=2时,L2-norm为:

        

    当k=1时,L1-norm为:

        

    还有一种归一化方式L2-Hys:

        首先进行L2-norm,然后进行截短(即值被限制为v - 0.2v之间),然后再归一化。

    HOGDescriptor API

    gpu::HOGDescriptor::HOGDescriptor

    创建HOG描述符和检测器。

    C++: gpu::HOGDescriptor::HOGDescriptor(Size win_size=Size(64, 128), Size block_size=Size(16, 16), Sizeblock_stride=Size(8, 8), Size cell_size=Size(8, 8), int nbins=9, double win_sigma=DEFAULT_WIN_SIGMA, doublethreshold_L2hys=0.2, bool gamma_correction=true, int nlevels=DEFAULT_NLEVELS)
    参数
    • win_size – 检测窗大小。需要和块的大小、步长匹配。
    • block_size – 块的大小。需要和细胞大小匹配。目前只支持(16,16)的大小。
    • block_stride – 块的步长,必须是细胞大小的整数倍。
    • cell_size – 细胞大小。目前只支持(8, 8)的大小。
    • nbins – 投票箱的个数。目前只支持每个细胞9个投票箱。
    • win_sigma – 高斯平滑窗口参数。
    • threshold_L2hys – L2-Hys归一化收缩率。
    • gamma_correction – 伽马校正预处理标志,需要或不需要。
    • nlevels – 检测窗口的最大数目。

    gpu::HOGDescriptor::getDescriptorSize

    返回分类所需的系数的数目。

    C++: size_t gpu::HOGDescriptor::getDescriptorSize() const

    gpu::HOGDescriptor::getBlockHistogramSize

    返回块直方图的大小。

    C++: size_t gpu::HOGDescriptor::getBlockHistogramSize() const

    gpu::HOGDescriptor::setSVMDetector

    设置线性SVM分类器的系数。

    C++: void gpu::HOGDescriptor::setSVMDetector(const vector<float>& detector)

    gpu::HOGDescriptor::getDefaultPeopleDetector

    返回人的分类训练检测(默认的窗口大小)的默认系数。

    C++: static vector<float> gpu::HOGDescriptor::getDefaultPeopleDetector()

    gpu::HOGDescriptor::getPeopleDetector48x96

    返回人的分类训练检测(48*96窗口大小)的系数。

    C++: static vector<float> gpu::HOGDescriptor::getPeopleDetector48x96()

    gpu::HOGDescriptor::getPeopleDetector64x128

    返回人的分类训练检测(64*128窗口大小)的系数。

    C++: static vector<float> gpu::HOGDescriptor::getPeopleDetector64x128()

    gpu::HOGDescriptor::detect

    在没有多尺度的窗口执行对象检测

    C++: void gpu::HOGDescriptor::detect(const GpuMat& img, vector<Point>& found_locations, double hit_threshold=0, Sizewin_stride=Size(), Size padding=Size())
    Parameters:
    • img – 源图像。只支持CV_8UC1CV_8UC4数据类型
    • found_locations – 检测出的物体的边缘。
    • hit_threshold – 特征向量和SVM划分超平面的阀值距离。通常它为0,并应由检测器系数决定。但是,当系数被省略时,可以手动指定它。
    • win_stride – 窗口步长,必须是块步长的整数倍。
    • padding – 模拟参数,使得CUP能兼容。目前必须是(0,0)。

    gpu::HOGDescriptor::detectMultiScale

    在多尺度窗口中执行对象检测。

    C++: void gpu::HOGDescriptor::detectMultiScale(const GpuMat& img, vector<Rect>& found_locations, double hit_threshold=0, Size win_stride=Size(), Size padding=Size(), double scale0=1.05, int group_threshold=2)
    参数
    • img – 源图像。只支持CV_8UC1CV_8UC4数据类型
    • found_locations – 检测出的物体的边缘。
    • hit_threshold – 特征向量和SVM划分超平面的阀值距离。通常它为0,并应由检测器系数决定。但是,当系数被省略时,可以手动指定它。
    • win_stride – 窗口步长,必须是块步长的整数倍。
    • padding – 模拟参数,使得CUP能兼容。目前必须是(0,0)。
    • scale0 – 检测窗口增长参数。
    • group_threshold – 调节相似性系数的阈值。检测到时,某些对象可以由许多矩形覆盖。 0表示不进行分组。

    gpu::HOGDescriptor::getDescriptors

    返回整个图片的块描述符。

    C++: void gpu::HOGDescriptor::getDescriptors(const GpuMat& img, Size win_stride, GpuMat& descriptors, intdescr_format=DESCR_FORMAT_COL_BY_COL)
    参数
    • img – 源图像。只支持CV_8UC1CV_8UC4数据类型
    • win_stride – 窗口步长,必须是块步长的整数倍。
    • descriptors – 描述符的2D数组。
    • descr_format –

      描述符存储格式:

      • DESCR_FORMAT_ROW_BY_ROW - 行存储。
      • DESCR_FORMAT_COL_BY_COL - 列存储。

    这个函数主要用于分类学习。

    检测代码

    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/gpu/gpu.hpp>
    
    #include <stdio.h>
    
    using namespace cv;
    
    int main(int argc, char** argv){
        Mat img;
        vector<Rect> found;
    
         img = imread(argv[1]);
    
        if(argc != 2 || !img.data){
            printf("没有图片\n");
            return -1;
        }
    
        HOGDescriptor defaultHog;
        defaultHog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
    
        //进行检测
        defaultHog.detectMultiScale(img, found);
    
        //画长方形,框出行人
        for(int i = 0; i < found.size(); i++){
            Rect r = found[i];
            rectangle(img, r.tl(), r.br(), Scalar(0, 0, 255), 3);
        }
    
        
        namedWindow("检测行人", CV_WINDOW_AUTOSIZE);
        imshow("检测行人", img);
    
        waitKey(0);
    
        return 0;
    }

    这段代码虽然可以检测出行人,但是可能会出现,一个人身上有几个框框。例如下图左边的行人:

    所以我们需要对found进行筛选,把那些被嵌套的长方形去除。

    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/gpu/gpu.hpp>
    
    #include <stdio.h>
    
    using namespace cv;
    
    int main(int argc, char** argv){
        Mat img;
        vector<Rect> found, foundRect;
    
         img = imread(argv[1]);
    
        if(argc != 2 || !img.data){
            printf("没有图片\n");
            return -1;
        }
    
        HOGDescriptor defaultHog;
        defaultHog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
    
        //进行检测
        defaultHog.detectMultiScale(img, found);
    
        //遍历found寻找没有被嵌套的长方形
        for(int i = 0; i < found.size(); i++){
            Rect r = found[i];
    
            int j = 0;
            for(; j < found.size(); j++){
                //如果时嵌套的就推出循环
                if( j != i && (r & found[j]) == r)
                    break;
            }
            if(j == found.size()){
                foundRect.push_back(r);
            }
        }
    
        //画长方形,圈出行人
        for(int i = 0; i < foundRect.size(); i++){
            Rect r = foundRect[i];
            rectangle(img, r.tl(), r.br(), Scalar(0, 0, 255), 3);
        }
    
        
        namedWindow("检测行人", CV_WINDOW_AUTOSIZE);
        imshow("检测行人", img);
    
        waitKey(0);
    
        return 0;
    } 

    PS:输入图片不能比检测窗还小,这样会抛出错误的。

    参考资料

    Histograms of Oriented Gradients for Human Detection . Navneet Dalal & Bill Triggs . 2005

    Histograms of Oriented Gradients for Human Detection Talk . Navneet Dalal & Bill Triggs

    OpenCV HOGDescriptor 参数图解 . Excalibur . 2011-03-11

  • 相关阅读:
    Mybatis 的 xml 文件语法错误,启动项目时控制台一直循环解析但是不打印错误
    在一个由 'L' , 'R' 和 'X' 三个字符组成的字符串(例如"RXXLRXRXL")中进行移动操作。一次移动操作指用一个"LX"替换一个"XL",或者用一个"XR"替换一个"RX"。现给定起始字符串start和结束字符串end,请编写代码,当且仅当存在一系列移动操作使得start可以转换成end时, 返回True。
    【Important】数据库索引原理
    服务化的演变和负载均衡
    【问题集】redis.clients.jedis.exceptions.JedisDataException: ERR value is not an integer or out of range
    【Spring】Spring中用到的设计模式
    【设计模式】责任链模式
    【!Important】Zookeeper用来做什么的,有几种类型的节点
    【!Important】如何保证线程执行的先后顺序
    【!Important】Java线程死锁查看分析方法
  • 原文地址:https://www.cnblogs.com/justany/p/2790548.html
Copyright © 2011-2022 走看看