积分图(三) - Boxfilter 的实现过程分析
Boxfilter 快速计算
它可以使复杂度为O(MN)的求和,求方差等运算降低到O(1)或近似于O(1)的复杂度,它的缺点是不支持多尺度。
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 ( buff 只是用来缓存中间变量的)中。以 (0,0) 位置为例进行说明:首先将绿色矩形内的每一列像素求和,结果放在 buff 内(红色方块),再对蓝色矩形内的像素求和,结果即为紫色特征矩形内的像素和,把它存放到数组A中,如此便完成了第一次求和运算。
4、每次紫色矩形向右移动时,实际上就是求对应的蓝色矩形的像素和,此时只要把上一次的求和结果减去蓝色矩形内的第一个红色块,再加上它右面的一个红色块,就是当前位置的和了,用公式表示 sum[i] = sum[i-1] - buff[j] + buff[j+m].
5、当紫色矩形移动到行末时,需要对 buff 进行更新。因为整个绿色矩形下移了一个像素,所以对于每个buff[i], 需要加上一个新进来的像素,再减去一个出去的像素,然后便开始新的一行的计算了。
Boxfilter 的初始化过程非常快速,每个矩形的计算基本上只需要一加一减两次运算。从初始化的计算速度上来说,Boxfilter 比 Integral Image 要快一些,大约 25%。在具体求某个矩形特征时,Boxfilter 比 Integral Image 快 4 倍,所谓的 4 倍其实就是从 4 次加减运算降低到 1 次,虽然这个优化非常渺小,但是把它放到几层大循环里面,还是能节省一些时间的。对于那些实时跟踪检测算法,一帧的处理时间要严格在 40ms 以下,正是这些细小的优化决定了程序的效率,积少成多,聚沙成塔。
下面的程序是Boxfilter的示例代码,谨供参考
.hpp
#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;
}
参考
积分图(三) - Boxfilter 的实现过程分析的更多相关文章
- 【AdaBoost算法】积分图代码实现
一.积分图介绍 定义:图像左上方的像素点值的和: 在Adaboost算法中可用于加速计算Haar或MB-LBP特征值,如下图: 二.代码实现 #include <opencv/highgui.h ...
- 浅析人脸检测之Haar分类器方法:Haar特征、积分图、 AdaBoost 、级联
浅析人脸检测之Haar分类器方法 一.Haar分类器的前世今生 人脸检测属于计算机视觉的范畴,早期人们的主要研究方向是人脸识别,即根据人脸来识别人物的身份,后来在复杂背景下的人脸检测需求越来越大,人脸 ...
- 目标检测之积分图---integral image 积分图2
前面在图像处理一栏中涉及到boxfilter 的时候,简单介绍过积分图,就是每个像素点是左边和上边的累加和,这样的话可以方便均值和方差,以及直方图统计的相关运算,这里再次结合网络资源重新单独对积分图做 ...
- geotrellis使用(三)geotrellis数据处理过程分析
之前简单介绍了geotrellis的工作过程以及一个简单的demo,最近在此demo的基础上实现了SRTM DEM数据的实时分析以及高程实时处理,下面我就以我实现的上述功能为例,简单介绍一下geotr ...
- 积分图实现均值滤波的CUDA代码
没想到我2010年买的笔记本显卡GT330M 竟然还能跑CUDA,果断小试了一把,环境为CUDA6.5+VS2012,写了一个积分图实现均值滤波.类似于OpenCV的blur()函数. 使用lena. ...
- Matlab 快速多通道积分图计算函数
所谓快速多通道积分图计算,其实就是 cumsum2D. 我写了一个比较快的版本(比 VLFeat 的快),用 mex 编译一下就能用了. 代码 #include <string.h> #i ...
- 积分图(二) - Block - Match(统计)滤波器
原文地址(英文) 积分图 是 [Crow(1984 年)] 提出的用于提高多尺度透视投影中纹理的渲染速度的一种技术. 积分图最流行的应用是 快速归一化互相关 (fast normalized cros ...
- Linux内核分析(三)内核启动过程分析——构造一个简单的Linux系统
一.系统的启动(各历史节点) 在最开始的时候,计算机的启动实际上依靠一段二进制码,可以这么理解,他并不是一个真正的计算机启动一道程序.计算机在开始加电的时候几乎是没有任何用处的,因为RAM芯片中包括的 ...
- UML类图三
2. 依赖关系 依赖(Dependency)关系是一种使用关系,特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系.大多数情况下,依赖关系体现在某个类的 ...
随机推荐
- 使用Hot Chocolate和.NET 6构建GraphQL应用(3) —— 实现Query基础功能
系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在本文中,我们通过一个简单的例子来看一下如何实现一个最简单的GraphQL的接口. 实现 引入Hot Cho ...
- [JavaWeb]Log4j的前因后果
Log4j的前因后果 简介 Log4j的进化史 Log4J的三大组件: Logger:日志记录器,负责收集处理日志记录 (如何处理日志) Appender:日志输出目的地,负责日志的输出 (输出到什么 ...
- Mac 屏幕录制Gif 制作 By-胡罗
一.视频录制 1)使用Mac系统自带的QuickTime进行屏幕录像 手动打开(如下图) 详细 Mac 基础教程:如何使用 Mac 系统原生的屏幕录制功能 相关快捷键 option+command+n ...
- WebLogic11g- 集群化
其实网上关于weblogic集群的资料非常多[大部分都是从创建新的domain开始,我这篇先介绍怎么样把原本普通的domain改造为集群环境],如果觉得不够,可以啃weblogic的官方文档.下面给几 ...
- 实现反向代理客户端IP透传
默认情况下,使用反向代理时,后端服务器只能看到访问是从反向代理服务器的IP,无法真正识别到客户端IP.通过配置IP透传实现后端服务器识别到客户端真实IP. 一.Apache后端服务器部署 1.1 安装 ...
- WebGPU 中消失的 FBO 和 RBO
目录 1 WebGL 中的 FBO 与 RBO 1.1 帧缓冲对象(FramebufferObject) 1.2 颜色附件与深度模板附件的真正载体 1.3 FBO/RBO/WebGLTexture 相 ...
- 4.Flink实时项目之数据拆分
1. 摘要 我们前面采集的日志数据已经保存到 Kafka 中,作为日志数据的 ODS 层,从 kafka 的ODS 层读取的日志数据分为 3 类, 页面日志.启动日志和曝光日志.这三类数据虽然都是用户 ...
- 《PHP程序员面试笔试宝典》——如何回答算法设计问题?
如何巧妙地回答面试官的问题? 本文摘自<PHP程序员面试笔试宝典> 程序员面试中的很多算法设计问题,都是历年来各家企业的"炒现饭",不管求职者以前对算法知识掌握得是否扎 ...
- suse 12 脚本部署docker(二进制文件)
suse-linux:~ # cat /etc/issue Welcome to SUSE Linux Enterprise Server 12 SP3 (x86_64) - Kernel \r (\ ...
- Spring Cloud之微服务注册到Eureka Server集群后访问改造
上篇Spring Cloud之服务注册中心搭建Eureka Server服务注册中⼼ - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)已经已经成功将两个微服务注册到集群中,那么能正常能与注 ...