• 【计算机视觉】极限优化:Haar特征的另一种的快速计算方法—boxfilter


    这种以Boxfilter替代integral image 的方法很难使用到haar、LBP等特征检测中,因为像下面说的,它不支持多尺度,也就是说所提取的特征必须是同一个大小,最起码同一个宽高比的,这一点对宽高不定的haar特征、LBP特征都有很大的限制,但对于HOG特征因为尺度不像另外两个那样灵活,还是有迹可循的。采长补短


    申明:以下非笔者原创,原文转载自:http://www.cnblogs.com/easymind223/archive/2012/11/13/2768680.html


    这个项目大概是在2年前了,因为要用嵌入式编程,所以无法用opencv的库函数,一切算法纯靠手写(是不是很坑爹?),其中一部分程序需要计算Haar特征,于是就有了下面的故事:

      在模式识别领域,Haar特征是大家非常熟悉的一种图像特征了,它可以应用于许多目标检测的算法中。与Haar相似,图像的局部矩形内像素的和、平方和、均值、方差等特征也可以用类似Haar特征的计算方法来计算。这些特征有时会频繁的在某些算法中使用,因此对它的优化势在必行。Boxfilter就是这样一种优化方法,它可以使复杂度为O(MN)的求和,求方差等运算降低到O(1)或近似于O(1)的复杂度,它的缺点是不支持多尺度

      第一个提出Haar特征快速计算方法的是CVPR2001上的那篇经典论文Rapid Object Detection using a Boosted Cascade of Simple Features ,它提出了integral image的概念,这个方法使得图像的局部矩形求和运算的复杂度从O(MN)下降到了O(4)。它的原理很简单:首先建立一个数组A,宽高与原图像相等,然后对这个数组赋值,每个元素的值A[i]赋为该点与图像原点所构成的矩形中所有像素的和。初始化之后,想要计算某个矩形像素和的时候可以采用如下方法:如图D矩形的像素和就等于A[4] – A[2] – A[3] + A[1],共4次运算,即O(4)。Integral Image极大的提高了Haar特征的计算速度,它的优点在于能够快速计算任意大小的矩形求和运算

      Boxfilter的原理有点类似Integral Image,而且比它还要快,但是实现步骤比较复杂。在计算矩形特征之前,Boxfilter与Integral Image都需要对图像进行初始化(即对数组A赋值),不同于Integral Image, Boxfilter的数组A中的每个元素的值是该像素邻域内的像素和(或像素平方和),在需要求某个矩形内像素和的时候,直接访问数组中对应的位置就可以了。因此可以看出它的复杂度是O(1)。

     

    Boxfilter的初始化过程如下:(此处较繁琐,如睡意来袭可以略过)

    1、给定一张图像,宽高为(M,N),确定待求矩形模板的宽高(m,n),如图紫色矩形。图中每个黑色方块代表一个像素,红色方块是假想像素。

    2、开辟一段大小为M的数组,记为buff, 用来存储计算过程的中间变量,用红色方块表示

    3、将矩形模板(紫色)从左上角(0,0)开始,逐像素向右滑动,到达行末时,矩形移动到下一行的开头(0,1),如此反复,每移动到一个新位置时,计算矩形内的像素和,保存在数组A中。以(0,0)位置为例进行说明:首先将绿色矩形内的每一列像素求和,结果放在buff内(红色方块),再对蓝色矩形内的像素求和,结果即为紫色特征矩形内的像素和,把它存放到数组A中,如此便完成了第一次求和运算。

    4、每次紫色矩形向右移动时,实际上就是求对应的蓝色矩形的像素和,此时只要把上一次的求和结果减去蓝色矩形内的第一个红色块,再加上它右面的一个红色块,就是当前位置的和了,用公式表示 sum[i] = sum[i-1] - buff[x-1] + buff[x+m-1]

    5、当紫色矩形移动到行末时,需要对buff进行更新。因为整个绿色矩形下移了一个像素,所以对于每个buff[i], 需要加上一个新进来的像素,再减去一个出去的像素,然后便开始新的一行的计算了。

     

      Boxfilter的初始化过程非常快速,每个矩形的计算基本上只需要一加一减两次运算。从初始化的计算速度上来说,Boxfilter比Integral Image要快一些,大约25%。在具体求某个矩形特征时,Boxfilter比Integral Image快4倍,所谓的4倍其实就是从4次加减运算降低到1次,虽然这个优化非常渺小,但是把它放到几层大循环里面,还是能节省一些时间的。对于那些实时跟踪检测算法,一帧的处理时间要严格在40ms以下,正是这些细小的优化决定了程序的效率,积少成多,聚沙成塔。

     

    下面的程序是Boxfilter的示例代码,谨供参考

    .h

    复制代码
    #pragma once
    
    typedef unsigned char uchar;
    
    class Boxfilter
    {
    public:
        Boxfilter(void);
        ~Boxfilter(void);
    
        void init(int width, int height, int mwidth=5, int mheight=5);
        void boxfilter(unsigned char* img);
    public:
        float    getMean(int x, int y);    //以x,y为中心点,mwidth,mheight为直径的局部区域,下同
        float    getVar(int x, int y);
        int        getSum(int x, int y);
        int        getSquareSum(int x, int y);
        int        getLocalSize();
    
    private:
        int mwidth ;
        int mheight ;
        unsigned char* img;
        int width;
        int height;
        int* f_sum;
        int* f_sum2;
    
    };
    复制代码

    .cpp

    复制代码
    #include "Boxfilter.h"
    #include <assert.h>
    #include <string>
    
    int* buff = 0;
    int* buff2 = 0;
    int boxwidth;
    int boxheight;
    
    Boxfilter::Boxfilter(void)    
    {    
        f_sum = 0;
        f_sum2 = 0;
    }
    
    Boxfilter::~Boxfilter(void)
    {
        if(f_sum)    delete[] f_sum;
        if(f_sum2)    delete[] f_sum2;
        if(buff)    delete[] buff;
        if(buff2)    delete[] buff2;
    }
    
    void Boxfilter::init(int width, int height, int mwidth, int mheight)
    {
        this->mwidth = mwidth;
        this->mheight = mheight;
        this->width = width;
        this->height = height;
    
        boxwidth = width - mwidth;
        boxheight = height - mheight;
        f_sum = new int[boxwidth *boxheight];
        f_sum2 = new int[boxwidth *boxheight];
    
        buff = new int[width];
        buff2= new int[width];
    }
    
    void Boxfilter::boxfilter (unsigned char* img)
    {
        int j,x,y;
    
        memset(buff, 0, width *sizeof(int));
        memset(buff2, 0, width *sizeof(int));
        memset(f_sum, 0, boxwidth *boxheight);
        memset(f_sum2, 0, boxwidth *boxheight);
    
        for(y=0; y<mheight; y++)
        {
            for(x=0; x<width; x++)
            {
                uchar pixel = img[y *width + x];
                buff[x] += pixel;
                buff2[x] += pixel*pixel;
            }
        }
    
        for(y=0; y<height - mheight;y++)
        {
            int Xsum=0;
            int Xsum2=0;
    
            for(j=0; j<mwidth; j++)
            {
                Xsum += buff[j];
                Xsum2 += buff2[j];
            }
    
            for(x=0; x<width - mwidth; x++)
            {
                if(x!=0)
                {
                    Xsum = Xsum-buff[x-1]+buff[mwidth-1+x];
                    Xsum2 = Xsum2-buff2[x-1]+buff2[mwidth-1+x];
                }
                f_sum[y*(width - mwidth)+x] = (float) Xsum ;
                f_sum2[y*(width - mwidth)+x] = Xsum2;            
            }
    
            for(x=0; x<width; x++)
            {
                uchar pixel = img[y *width + x];    
                uchar pixel2 = img[(y+mheight) *width + x];    
                buff[x] = buff[x] - pixel + pixel2;    
                buff2[x] = buff2[x] - pixel*pixel +    pixel2*pixel2;    
            }
        }
    }
    
    float Boxfilter::getMean(int x, int y)
    {
        return getSum(x,y) / (float)(mwidth*mheight);
    }
    
    float Boxfilter::getVar(int x, int y)
    {
        float mean = getMean(x, y);
        return (float)getSquareSum(x, y)/(mwidth *mheight) - mean*mean;
    }
    
    int Boxfilter::getSquareSum(int x, int y)
    {
        if(y>mheight/2 && y<height - mheight/2 && x>mwidth/2 && x<width - mwidth/2)
            return f_sum2[(y - mheight/2) *boxwidth + (x - mwidth/2)];
        else
            return -1;
    }
    
    int    Boxfilter::getSum(int x, int y)
    {
        if(y>mheight/2 && y<height - mheight/2 && x>mwidth/2 && x<width - mwidth/2)
            return f_sum[(y - mheight/2) *boxwidth + (x - mwidth/2)];
        else
            return -1;
    }
    
    int    Boxfilter::getLocalSize()
    {
        return mwidth > mheight ? mwidth : mheight;
    }
    复制代码

    测试用例

    复制代码
    // cv2.4 test.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include "opencv2/opencv.hpp"
    #include "Boxfilter.h"
    #include <iostream>
    using namespace cv;
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        Mat src = imread("C:\Documents and Settings\Administrator\桌面\img1.png",0);
    
        Boxfilter box;
        box.init(src.cols, src.rows, 5, 5);
        box.boxfilter((uchar*)src.data);
    
        int x = 50, y = 50;
        float a = box.getMean(x, y);    //求出以(x,y)为中心的矩形的均值
        float b = box.getVar(x, y);
        int c = box.getSum(x, y);
        int d = box.getSquareSum(x, y);
        int e = box.getLocalSize();
    
        cout<<"mean: "        <<a<<endl;
        cout<<"var:    "        <<b<<endl;
        cout<<"sum:    "        <<c<<endl;
        cout<<"squaresum: "    <<d<<endl;
        cout<<"size:    "    <<e<<endl;
    
        getchar();
        return 0;
    }

  • 相关阅读:
    SQL学习
    FOR XML PATH
    IOS学习网址
    weak nonatomic strong等介绍(ios)
    UVALive3045 POJ2000 ZOJ2345 Gold Coins
    UVA713 UVALive5539 POJ1504 ZOJ2001 Adding Reversed Numbers
    UVA713 UVALive5539 POJ1504 ZOJ2001 Adding Reversed Numbers
    UVA439 POJ2243 HDU1372 ZOJ1091 Knight Moves【BFS】
    UVA439 POJ2243 HDU1372 ZOJ1091 Knight Moves【BFS】
    UVA10905 Children's Game
  • 原文地址:https://www.cnblogs.com/huty/p/8518798.html
Copyright © 2020-2023  润新知