【NCC】之二:积分图加速均值计算

积分图(Integral Image)是一种高效的数据结构,用于快速计算图像矩形区域的像素和。通过预先计算,可以实现区域均值和方差的即时计算,常用于模板匹配和NCC等算法。示例展示了积分图如何构建,并提供了计算源图矩形区域均值和方差的公式。使用积分图可以显著加速NCC计算,与OpenCV实现相比,速度有显著提升。
摘要由CSDN通过智能技术生成

积分图 integral image

1. 原理:

Summed Area Table是一种数据结构和算法,用于快速有效地生成网格矩形子集中的值总和。在图像处理领域,它也被称为积分图像。

记源图像为 I m × n I_{m \times n} Im×n,积分图为SAT(Summed Area Table)则
S A T ( x , y ) = Σ i ≤ x , j ≤ y I ( i , j ) SAT(x,y) = \underset{i\leq x,j\leq y}{\Sigma }I(i,j) SAT(x,y)=ix,jyΣI(i,j)

S Q A T ( x , y ) = Σ i ≤ x , j ≤ y I ( i , j ) 2 SQAT(x,y) = \underset{i\leq x,j\leq y}{\Sigma }I(i,j)^2 SQAT(x,y)=ix,jyΣI(i,j)2

  • 计算积分图的递推式
    S A T ( x , y ) = S A T ( x − 1 , y ) + S A T ( x , y − 1 ) − S A T ( x − 1 , y − 1 ) + I ( x , y ) SAT(x,y) = SAT(x-1,y) + SAT(x,y-1) - SAT(x-1,y-1) + I(x,y) SAT(x,y)=SAT(x1,y)+SAT(x,y1)SAT(x1,y1)+I(x,y)
    上面的公式中起始项必须从第二行第二列开始,因为递推式中包含-1项,也就是需要单独把第一行和第一列先单独计算出来,后面的所有点都可以用递推式写出来。

  • 一点漂亮的改进
    让积分图比原图多一行一列:上面一行,左边一列,且行列初始值为0!这样的话就可以不需要考虑-1项完全采用递推式得到积分图!opencv就是这么干的

2. 示例

在这里插入图片描述

图片摘自Wikipedia。1为源图,2为积分图每一个像素点的值都是它在源图上对应位置的上面、左边所有像素值的和。

3. 计算区域均值

当要计算源图中的某个矩形区域的像素和时可以用查表的方式快速计算,构建好积分图后求区域和的复杂度将是 O ( 1 ) O(1) O(1)

设在源图中的矩形 R R R左上角坐标为 ( t p x , t p y ) (tpx,tpy) (tpx,tpy),右下角坐标为 ( b t x , b t y ) (btx,bty) (btx,bty),则计算该矩形区域内所有像素值和可以写为下式。

  • 区域像素值之和
    s u m = S A T ( b t x , b t y ) − S A T ( t p x − 1 , b t y ) − S A T ( b t x , t p y − 1 ) + S A T ( t p x − 1 , t p y − 1 ) sum = SAT(btx,bty) - SAT(tpx-1,bty) - SAT(btx,tpy-1) + SAT(tpx-1,tpy-1) sum=SAT(btx,bty)SAT(tpx1,bty)SAT(btx,tpy1)+SAT(tpx1,tpy1)
  • 区域均值
    m e a n = s u m ( b t y − t p y + 1 ) ( b t x − t p x + 1 ) mean = \frac{sum}{(bty-tpy+1)(btx-tpx+1)} mean=(btytpy+1)(btxtpx+1)sum

4. 计算区域方差

【NCC】之一:从Pearson相关系数到模板匹配的NCC方法中的 S x , y S_{x,y} Sx,y的方差计算为例
计算区域方差
σ ( S x , y ) = v a r ( S x , y ) = Σ i = 1 m Σ j = 1 n ( S x , y ( i , j ) − S x , y ˉ ) 2 m n \sigma(S_{x,y})=\sqrt{var(S_{x,y})}=\sqrt{\frac{\Sigma_{i=1}^{m}\Sigma_{j=1}^{n}{(S_{x,y}(i,j)-\bar{S_{x,y}}})^2}{mn}} σ(Sx,y)=var(Sx,y) =mnΣi=1mΣj=1n(Sx,y(i,j)Sx,yˉ)2
let A = Σ i = 1 m Σ j = 1 n ( S x , y ( i , j ) − S x , y ˉ ) 2 A = \Sigma_{i=1}^{m}\Sigma_{j=1}^{n}{(S_{x,y}(i,j)-\bar{S_{x,y}}})^2 A=Σi=1mΣj=1n(Sx,y(i,j)Sx,yˉ)2 则有
A = Σ i = 1 m Σ j = 1 n ( S x , y ( i , j ) 2 − 2 S x , y ˉ S x , y ( i , j ) + S x , y ˉ 2 ) = S Q A T ( m , n ) S x , y − 2 S x , y ˉ × S A T ( m , n ) S x , y + m n × S x , y ˉ 2 = S Q A T ( m , n ) S x , y − 2 S x , y ˉ × S A T ( m , n ) S x , y + S x , y ˉ × S A T ( m , n ) = S Q A T ( m , n ) S x , y − S x , y ˉ × S A T ( m , n ) S x , y \begin{aligned} \begin{aligned} A &=\Sigma_{i=1}^{m}\Sigma_{j=1}^{n}( S_{x,y}(i,j)^2 -2\bar{S_{x,y}}S_{x,y}(i,j) + \bar{S_{x,y}}^2) \\ &= \underset{S_{x,y}}{SQAT(m,n)} -\underset{S_{x,y}}{2\bar{S_{x,y}}\times SAT(m,n)} + mn\times \bar{S_{x,y}}^2 \\ &=\underset{S_{x,y}}{SQAT(m,n)}-\underset{S_{x,y}}{2\bar{S_{x,y}}\times SAT(m,n)} + \bar{S_{x,y}}\times SAT(m,n) \\ &=\underset{S_{x,y}}{SQAT(m,n)}-\underset{S_{x,y}}{\bar{S_{x,y}}\times SAT(m,n)} \end{aligned} \end{aligned} A=Σi=1mΣj=1n(Sx,y(i,j)22Sx,yˉSx,y(i,j)+Sx,yˉ2)=Sx,ySQAT(m,n)Sx,y2Sx,yˉ×SAT(m,n)+mn×Sx,yˉ2=Sx,ySQAT(m,n)Sx,y2Sx,yˉ×SAT(m,n)+Sx,yˉ×SAT(m,n)=Sx,ySQAT(m,n)Sx,ySx,yˉ×SAT(m,n)

5. 积分图示例

  • 原图
    原图

  • 积分图
    在这里插入图片描述

  • 平方积分图
    在这里插入图片描述

6. 计算积分图的源码

/**
 * @brief 计算输入图的积分图,为了提高计算效率,可以让积分图比输入图多一行一列,
 * 具体的就是在原图左边插入一列0,上面插入一行0,设原图为I,积分图为SAT(sum area of table)
 * 则:SAT(i,j) = SAT(i,j-1) + SAT(i-1,j) - SAT(i-1,j-1) + I(i,j)
 * 这样就不用考虑下边界的情况,省掉很多判断条件
 * 
 * @param image  : 输入图CV_8UC1,MxN
 * @param integral_image  : 积分图CV_32FC1,(M+1)x(N+1)
 * @return int 
 */
int integral(const cv::Mat &image,cv::Mat &integral_image)
{
    if(image.empty())
    {
        MYCV_ERROR(kImageEmpty,"image empty");
        return kImageEmpty;
    }

    int h = image.rows;
    int w = image.cols;
    integral_image = cv::Mat::zeros(cv::Size(w+1,h+1),CV_64FC1);

    //SAT(i,j) = SAT(i,j-1) + SAT(i-1,j) - SAT(i-1,j-1) + I(i,j)
    for(int i = 0; i < h ; i++)
    {
        const uchar *ps = image.ptr<uchar>(i);
        double *pd_m1 = integral_image.ptr<double>(i);//integral 的"上一行"
        double *pd = integral_image.ptr<double>(i+1); //integral 的"当前行"
        for(int j = 0; j < w; j++)
        {
            pd[j+1] = pd[j] + pd_m1[j+1] - pd_m1[j] + ps[j];
        }
    }

    return kSuccess;
}


/**
 * @brief 计算输入图的积分图,为了提高计算效率,可以让积分图比输入图多一行一列,
 * 具体的就是在原图左边插入一列0,上面插入一行0,设原图为I,积分图为SAT(summed area table)
 * 则:SAT(i,j) = SAT(i,j-1) + SAT(i-1,j) - SAT(i-1,j-1) + I(i,j)
 * SQAT(i,j) = SQAT(i,j-1) + SQAT(i-1,j) - SQAT(i-1,j-1) + I(i,j) * I(i,j)
 * 这样就不用考虑下边界的情况,省掉很多判断条件
 * 
 * @param image  : 输入图CV_8UC1,MxN
 * @param integral_image  : 积分图CV_32FC1,(M+1)x(N+1)
 * @param integral_sq : 平方的积分图CV_32FC1,(M+1)x(N+1)
 * @return int 
 */
int integral(const cv::Mat &image,cv::Mat &integral_image,cv::Mat &integral_sq)
{
     if(image.empty())
    {
        MYCV_ERROR(kImageEmpty,"image empty");
        return kImageEmpty;
    }

    int h = image.rows;
    int w = image.cols;
    integral_image = cv::Mat::zeros(cv::Size(w+1,h+1),CV_64FC1);
    integral_sq = cv::Mat::zeros(cv::Size(w+1,h+1),CV_64FC1);

    //SAT(i,j) = SAT(i,j-1) + SAT(i-1,j) - SAT(i-1,j-1) + I(i,j)
    //SQAT(i,j) = SQAT(i,j-1) + SQAT(i-1,j) - SQAT(i-1,j-1) + I(i,j) * I(i,j)
    for(int i = 0; i < h ; i++)
    {
        const uchar *ps = image.ptr<uchar>(i);
        double *pd_m1 = integral_image.ptr<double>(i);//integral 的"上一行"
        double *pd = integral_image.ptr<double>(i+1); //integral 的"当前行"
        double *pqd_m1 = integral_sq.ptr<double>(i);
        double *pqd = integral_sq.ptr<double>(i+1);
        for(int j = 0; j < w; j++)
        {
            pd[j+1] = pd[j] + pd_m1[j+1] - pd_m1[j] + (double)ps[j];
            pqd[j+1] = pqd[j] + pqd_m1[j+1] - pqd_m1[j] + (double)ps[j] * (double)ps[j];
        }
    }


    return kSuccess;
}

7. 用积分图加速NCC

采用积分图计算均值和方差

source image size w,h = (1095,680)
target image size w,h = (89,91)
my NCC run 5 times,average use 5699.000000 ms
min_value=-0.431418 , min_loc(x,y)=(799,210),    max_value=0.998323,max_loc(x,y)=(393,286)
opencv NCC run 5 times,average use 20.000000 ms
min_value=-0.431418 , min_loc(x,y)=(799,210),    max_value=0.998322,max_loc(x,y)=(393,286)

opencv的速度是这一版本的285倍,相比上一版882.78倍还是有点进步。

8. 补充

测试自己的积分图和opencv的积分图在数值上完全一致了,但是速度上opencv的是我的3-5倍,今天(2023年6月5日)采用IPP中的ippiSqrIntegral_8u32f64f_C1R()方法后速度就和opencv中的积分图一致了。IPP是不开源的,IPPICV是一个IPP提取出来的一个子库专门用在图像处理里面,现在已经打包在opencv中,在IPPICV中对应的函数为 ippicviSqrIntegral_8u32f64f_C1R
在这里插入图片描述

参考

[1] https://en.wikipedia.org/wiki/Summed-area_table
[2] 积分图(一) - 原理及应用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值