一、积分图概念
今天继续补充一些视觉特征里常用的基础算法概念。积分图计算Integral image compute,这是个很基础的一个图像预处理的算法,它是由Crow在1984年首次提出,是为了在多尺度透视投影中提高渲染速度。积分图算法是一种快速计算图像区域像素和(and平方和)的算法。它的核心思想就是对每一个图像建立起自己的积分查找表(本质是一个查找表LUT),在图像处理的阶段就可以根据预先建立积分图查找表直接查找从而实现对均值卷积的线性时间计算。做到了卷积执行的时间与窗口大小无关,优化各种复杂的检测算法的计算速度以提升性能。
二、积分图计算
那么什么是积分图?如下图所示。从直观来说,一张图是一个二维矩阵列,每个坐标位置保存的是对应位置的像素值;而积分图的每个位置保存的是 以图像区域左上角像素点为左上角顶点,以该像素点为右下角顶点的矩形中所包含的所有元素的像素值之和。
举一个简单的例子:一个3x3的图像Image其像素值如下表格1~9,那么根据积分图的概念,其对应的Integral Image每个位置对应的值如下表所示。
实际计算积分图的时候,为了提高计算效率,通常不会如上例子般对每一个位置都重新计算矩形区域包含的所有元素值之和,而是利用相邻位置点的积分值实现快速计算,如下图所示,点(x,y)的积分值可以使用点(u,y)与点(x,v)的积分值之和,然后减去重叠区域,也就是减去点(u,v)的积分值,最后再加上点(x,y)原来的像素值,最终得到点(x,y)的积分值。
上述原理的总结数学公式表示为:
II(x,y) = II(u,y) + II(x,v) - II(u,v) + Pixel(x,y)
实际计算时还需要考虑边界问题,也就是第一行和第一列的计算,真实图像是没有第0行和第0列的,所以在计算前,我们要先补充第0行和第0列,数值全为0,然后就可以代入上述的通用公式进行积分图的计算了。如下图所示
三、积分图的应用
那么整这么一个积分图,有什么作用呢?想想看这么一个场景,需要求图像中指定某块区域的积分,如图上(u,v)->(x,y)矩形内的积分,按正常的计算方式,得实时算m*n次加法(m = x - u, n = y - v),如果使用预计算了的积分图,那么矩形内的积分可简化为:
sum(m,n) = ii(x,y) - ii(x,v) - ii(u,y) + ii(u,v),(m=x - u, n=y - v)
从图中可以理解:-ii(x,v) 和 -ii(u,y)中多减去了一块ii(u,v)的区域,所以得加回来。
从数学公式中我们可以看到,利用积分图进行快速查表求指定区域的积分,就跟区域窗口的大小无关了,优化成2个加法1个减法,大大提高了计算性能。利用这一特性可以大大优化很多特征检测算法的效率。
OpenCV官方提供两种积分图函数integral():
//API一
void integral( InputArray src, //输入图像
OutputArray sum, //和表
int sdepth = -1,); //和表深度
//API二
void integral( InputArray src, //输入图像
OutputArray sum, //和表
OutputArray sqsum, //平方和表
int sdepth = -1, //和表深度,一般为CV_32S
int sqdepth = -1 ); //平方和表深度,一般为CV_32F
调用例子:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int get_block_sum(Mat &sum, int x1, int y1, int x2, int y2) {
int BottomRight = sum.at<int>(x2, y2);
int TopLeft = sum.at<int>(x1, y1);
int TopRight = sum.at<int>(x1, x2);
int BottomLeft = sum.at<int>(x2, y1);
int sum_value = (BottomRight + TopLeft - TopRight - BottomLeft);
return sum_value;
}
int main() {
Mat src, sum, sqrsum;
src = imread("1.png", 0);
integral(src, sum, sqrsum, CV_32S, CV_32F);
normalize(sum, sum, 0, 255, NORM_MINMAX, CV_8UC1, Mat());
normalize(sqrsum, sqrsum, 0, 255, NORM_MINMAX, CV_8UC1, Mat());
imshow("原图", src);
imshow("和表积分图", sum);
imshow("平方和表积分图", sqrsum);
int LR = 4;
int LC = 4;
int width = 6;
int height = 6;
int value = get_block_sum(sum, LR, LC, LR + height, LC + width);
cout << value << endl;
waitKey(0);
return 0;
}