使输入P和出口Q尽恐怕相似

由上篇导向滤波算法分析,根据(5)~(8)式就足以计算输出图像Q

由上篇导向滤波算法分析,根据(5)~(8)式就可以总结输出图像Q

本文从数学上演绎导向滤波的算法,其算法的实际实以后下一篇导向滤波算法的落到实处介绍。

图片 1  (5)

图片 2  (5)

设指点图G,输入图像P,输出图像Q。导向滤波的目的是驱动输入P和出口Q尽大概相同,同时纹理部分和率领图G相似。

图片 3  (6)

图片 4  (6)

为了满意第三个对象,使输入P和出口Q尽恐怕相似,大家渴求最小化平方差

图片 5  (7)

图片 6  (7)

图片 7

图片 8  (8)

图片 9  (8)

为了餍足第二个目的,使输出图像Q的纹路和指导图G的纹理相似,我们要

其中图片 10图片 11,/ai和/bi的结果要计算有所覆盖了像素i的窗口Wk的ak和bk的平均值。除了用平均值,在实际利用中,作者还见到过其余的统计/ai和/bi的不二法门。比如依照像素i在窗口Wk的职位,给予差其他权重。如若i距离窗口Wk的为主地点越远,则给予ak和bk越低的权重。倘诺i位于窗口中央,则ak和bk有参天的权重。最常用的就是用高斯分布来予以不一致的权重。那样考虑到了附近像素距离远近对i影响的大小,最后的结果会更可相信一些。

其中图片 12图片 13,/ai和/bi的结果要计算有所覆盖了像素i的窗口Wk的ak和bk的平均值。除了用平均值,在其实使用中,作者还见到过其余的总括/ai和/bi的主意。比如依据像素i在窗口Wk的职位,给予差别的权重。假诺i距离窗口Wk的宗旨岗位越远,则给予ak和bk越低的权重。借使i位于窗口中央,则ak和bk有最高的权重。最常用的就是用高斯分布来予以差距的权重。那样考虑到了隔壁像素距离远近对i影响的尺寸,最后的结果会更可看重一些。

图片 14

此处本身可能用最简便易行的平均值的主意来总括/ai和/bi。大家前边早已假定了在窗口Wk内,ak和bk是常数,因此ak和bk只和Wk的职位有关。取Wk为半径为r的方形窗口,使窗口的基本像素地点遍历整个图像,那么就使Wk取到了区其余持有地点,在各样位置总计出相应的ak和bk。所有的ak和bk重组了和输入图像P相同长宽维度的数量集合,记为A和B。对于自由像素i,/ai和/bi即分别为以i为骨干半径r的窗口Wk内A和B的数码均值,这不正是大家熟识的图像均值模糊的算法吗?而图像均值模糊有二种相比较成熟的快捷算法,比如积分图算法和依照直方图的飞速算法。只要有了A和B,就足以方便的接纳均值模糊得出/ai和/bi,从而接纳(8)计算出输出图像Q。

那里作者要么用最简单易行的平均值的法子来总计/ai和/bi。大家面前已经假定了在窗口Wk内,ak和bk是常数,因此ak和bk只和Wk的职位有关。取Wk为半径为r的方形窗口,使窗口的主干像素地方遍历整个图像,那么就使Wk取到了差距的拥有职位,在每一个岗位统计出相应的ak和bk。所有的ak和bk重组了和输入图像P相同长宽维度的数量集合,记为A和B。对于自由像素i,/ai和/bi即分别为以i为基本半径r的窗口Wk内A和B的数额均值,那不正是大家耳熟能详的图像均值模糊的算法吗?而图像均值模糊有二种相比较早熟的很快算法,比如积分图算法和基于直方图的登时算法。只要有了A和B,就可以便宜的利用均值模糊得出/ai和/bi,从而接纳(8)总结出输出图像Q。

积分得到

为了计算A和B,从(6)式看到,需求各自总括输入图像P和导向图G的均值模糊结果。而(5)式需求计算导向图G的方差,还有P和G的协方差。方差和协方差都关乎到乘积的求和计量,可以由上面的公式,通过积分图来总结。那八个公式很简单推导出来,就不赘述了。

为了计算A和B,从(6)式看到,必要各自统计输入图像P和导向图G的均值模糊结果。而(5)式须求统计导向图G的方差,还有P和G的协方差。方差和协方差都关乎到乘积的求和总计,可以由上面的公式,通过积分图来测算。那三个公式很不难推导出来,就不赘述了。

图片 15

图片 16

图片 17

考虑多个小窗口Wk,在Wk内觉得a,b保持不变,设为ak,bk。Wk内的像素满意

图片 18

图片 19

图片 20  (1)

1个平方和,一个乘积和都可以用积分图来计量。只是要留意当图像丰富大的时候,要用合适的数据类型。借使像素的数量范围是0~255的整型,假设平方积分图用叁拾叁个人的整型数据,那么只好扶助最大256×256尺寸的图像。超越这么些分寸,就非得要用6三位的整型了。上边给出使用模板函数的乘积积分图函数,可以依据必要运用差别的数据类型。p1和p2是图像数据指针,当它们对准相同的数目时,这几个函数就变成了平方积分图。注意积分图的长宽比原始数据的长宽都要大1。

一个平方和,多个乘积和都可以用积分图来计量。只是要留心当图像丰富大的时候,要用合适的数据类型。如果像素的数据范围是0~255的整型,即使平方积分图用三十3人的整型数据,那么只好资助最大256×256高低的图像。当先那么些分寸,就非得要用6几个人的整型了。上面给出使用模板函数的乘积积分图函数,可以根据要求动用区其他数据类型。p1和p2是图像数据指针,当它们对准相同的数额时,那几个函数就改为了平方积分图。注意积分图的长宽比原始数据的长宽都要大1。

把(1)代入第二个目标,使窗口内的像素同时满意上边八个尺码。

图片 21图片 22

图片 23图片 24

图片 25  (2)

/* Cumulative image of the multiplication of p1.*p2.
 p1 and p2 can be the same pointer and it becomes square cumulative image.
 The returned cumulative image MUST be freed by the caller! */
template <class T1, class T2, class T3>
BOOL MultiplyCumImage(T1 *p1, T2 *p2, T3 **ppCum)
{
    long i, j;
    long Width = GetWidth();
    long Height = GetHeight();
    long integral_len = (Width + 1)*(Height + 1);

    // Only allocate cumulative image memory when *ppCum is NULL
    if (*ppCum == NULL)
    {
        try { *ppCum = new T3[integral_len]; }
        catch (CException *pe)
        {
            pe->Delete();
            return FALSE;
        }
    }
    memset(*ppCum, 0, sizeof(T3)*integral_len);
    // The cumulative values of the leftmost and the topmost pixels are always 0.
    for (i = 1; i <= Height; i++)
    {
        T3 *prow, *puprow;
        prow = *ppCum + i*(Width + 1);
        puprow = *ppCum + (i - 1)*(Width + 1);
        T3 sum = 0;
        long up_row_idx = (i - 1)*Width;
        for (j = 1; j <= Width; j++)
        {
            long idx = up_row_idx + j - 1;
            sum += p1[idx] * p2[idx];
            prow[j] = puprow[j] + sum;
        }
    }
    return TRUE;
}
/* Cumulative image of the multiplication of p1.*p2.
 p1 and p2 can be the same pointer and it becomes square cumulative image.
 The returned cumulative image MUST be freed by the caller! */
template <class T1, class T2, class T3>
BOOL MultiplyCumImage(T1 *p1, T2 *p2, T3 **ppCum)
{
    long i, j;
    long Width = GetWidth();
    long Height = GetHeight();
    long integral_len = (Width + 1)*(Height + 1);

    // Only allocate cumulative image memory when *ppCum is NULL
    if (*ppCum == NULL)
    {
        try { *ppCum = new T3[integral_len]; }
        catch (CException *pe)
        {
            pe->Delete();
            return FALSE;
        }
    }
    memset(*ppCum, 0, sizeof(T3)*integral_len);
    // The cumulative values of the leftmost and the topmost pixels are always 0.
    for (i = 1; i <= Height; i++)
    {
        T3 *prow, *puprow;
        prow = *ppCum + i*(Width + 1);
        puprow = *ppCum + (i - 1)*(Width + 1);
        T3 sum = 0;
        long up_row_idx = (i - 1)*Width;
        for (j = 1; j <= Width; j++)
        {
            long idx = up_row_idx + j - 1;
            sum += p1[idx] * p2[idx];
            prow[j] = puprow[j] + sum;
        }
    }
    return TRUE;
}

个中ε是三个收拾大的ak的正则化参数。使(2)最小,满意

View
Code

View
Code

图片 26

 那样导向滤波完毕的显要难题都消除了,算法步骤如下:

 那样导向滤波完结的重大难点都化解了,算法步骤如下:

其中|W|是窗口Wk的像素总数。解得

  1. 统计率领图G的积分图和平方积分图
  2. 测算输入图像P的积分图, P和G的乘积积分图
  3. 用上两步得出的积分图总计P和G的均值,G的方差,P和G的协方差,窗口半径为r
  4. 然后用(5)(6)式总结周密图A和B
  5. 计算A和B的积分图
  6. 总结A和B的窗口半径r的均值,并用(8)式总括输出图Q
  1. 计量指点图G的积分图和平方积分图
  2. 算算输入图像P的积分图, P和G的乘积积分图
  3. 用上两步得出的积分图总结P和G的均值,G的方差,P和G的协方差,窗口半径为r
  4. 接下来用(5)(6)式总括周密图A和B
  5. 计算A和B的积分图
  6. 总结A和B的窗口半径r的均值,并用(8)式计算输出图Q

图片 27  (3)

 上边的参照代码中,pData存储输入和输出图像,pGuidedData指导图,radius领域半径

 上面的参考代码中,pData存储输入和输出图像,pGuidedData指导图,radius领域半径

图片 28  (4)

图片 29图片 30

图片 31图片 32

如果设/pk是输入图P在窗口Wk的平均值,μk和σk2是指点图G在窗口Wk的平均值和方差。大家发现

    long len = Width*Height;

    // Cululative image and square cululative for guided image G
    UINT64 *pCum = NULL, *pSquareCum = NULL;
    CumImage(pGuidedData, &pCum);
    MultiplyCumImage(pGuidedData, pGuidedData, &pSquareCum);

    // Allocate memory for a and b
    float *pa, *pb;
    pa = new float[len];
    pb = new float[len];
    memset(pa, 0, sizeof(float)*len);
    memset(pb, 0, sizeof(float)*len);

    UINT64 *pInputCum = NULL, *pGPCum = NULL;
    CumImage(pData, &pInputCum);
    MultiplyCumImage(pGuidedData, pData, &pGPCum);

    int field_size;
    UINT64 cum, square_cum;
    long uprow, downrow, upidx, downidx;        // In cumulative image
    long leftcol, rightcol;
    float g_mean, g_var;        // mean and variance of guided image
    long row_idx = 0;
    UINT64 p_cum, gp_cum;
    float p_mean;
    // Calculate a and b
    // Since we're going to calculate cumulative image of a and b, we have to calculate the whole image of a and b.
    for (i = 0; i < Height; i++)
    {
        // Check the boundary for radius
        if (i < radius) uprow = 0;
        else uprow = i - radius;
        upidx = uprow*(Width + 1);
        if (i + radius >= Height) downrow = Height;
        else downrow = i + radius + 1;
        downidx = downrow*(Width + 1);
        for (j = 0; j < Width; j++)
        {
            // Check the boundary for radius
            if (j < radius) leftcol = 0;
            else leftcol = j - radius;
            if (j + radius >= Width) rightcol = Width;
            else rightcol = j + radius + 1;
            field_size = (downrow - uprow)*(rightcol - leftcol);
            long p1, p2, p3, p4;
            p1 = downidx + rightcol;
            p2 = downidx + leftcol;
            p3 = upidx + rightcol;
            p4 = upidx + leftcol;
            // Guided image summary in the field
            cum = pCum[p1] - pCum[p2] - pCum[p3] + pCum[p4];
            // Guided image square summary in the field
            square_cum = pSquareCum[p1] - pSquareCum[p2] - pSquareCum[p3] + pSquareCum[p4];
            // Field mean
            g_mean = (float)(cum) / field_size;
            // Field variance
            g_var = float(square_cum) / field_size - g_mean * g_mean;
            // Summary of input image in the field
            p_cum = pInputCum[p1] - pInputCum[p2] - pInputCum[p3] + pInputCum[p4];
            // Input image field mean
            p_mean = float(p_cum) / field_size;
            // Multiply summary in the field
            gp_cum = pGPCum[p1] - pGPCum[p2] - pGPCum[p3] + pGPCum[p4];
            long idx = row_idx + j;
            pa[idx] = (float(gp_cum) / field_size - g_mean*p_mean) / (g_var + epsilon);
            pb[idx] = p_mean - g_mean*pa[idx];
        }
        row_idx += Width;
    }
    // not needed after this
    delete[] pCum;
    delete[] pSquareCum;
    delete[] pInputCum;
    delete[] pGPCum;

    // Cumulative image of a and b
    float *pCuma = NULL, *pCumb = NULL;
    CumImage(pa, &pCuma);
    CumImage(pb, &pCumb);

    // Finally calculate the output image q=ag+b
    float mean_a, mean_b;
    row_idx = Hstart*Width;
    for (i = Hstart; i < Hend; i++)
    {
        // Check the boundary for radius
        if (i < radius) uprow = 0;
        else uprow = i - radius;
        upidx = uprow*(Width + 1);
        if (i + radius >= Height) downrow = Height;
        else downrow = i + radius + 1;
        downidx = downrow*(Width + 1);
        for (j = Wstart; j < Wend; j++)
        {
            // Check the boundary for radius
            if (j < radius) leftcol = 0;
            else leftcol = j - radius;
            if (j + radius >= Width) rightcol = Width;
            else rightcol = j + radius + 1;
            field_size = (downrow - uprow)*(rightcol - leftcol);
            long p1, p2, p3, p4;
            p1 = downidx + rightcol;
            p2 = downidx + leftcol;
            p3 = upidx + rightcol;
            p4 = upidx + leftcol;
            // Field mean
            mean_a = (pCuma[p1] - pCuma[p2] - pCuma[p3] + pCuma[p4]) / field_size;
            // Field mean
            mean_b = (pCumb[p1] - pCumb[p2] - pCumb[p3] + pCumb[p4]) / field_size;
            // New pixel value
            long idx = row_idx + j;
            int value = int(mean_a*pGuidedData[idx] + mean_b);
            CLAMP0255(value);
            pData[idx] = value;
        }
        row_idx += Width;
    }

    delete[] pa;
    delete[] pb;
    delete[] pCuma;
    delete[] pCumb;
    long len = Width*Height;

    // Cululative image and square cululative for guided image G
    UINT64 *pCum = NULL, *pSquareCum = NULL;
    CumImage(pGuidedData, &pCum);
    MultiplyCumImage(pGuidedData, pGuidedData, &pSquareCum);

    // Allocate memory for a and b
    float *pa, *pb;
    pa = new float[len];
    pb = new float[len];
    memset(pa, 0, sizeof(float)*len);
    memset(pb, 0, sizeof(float)*len);

    UINT64 *pInputCum = NULL, *pGPCum = NULL;
    CumImage(pData, &pInputCum);
    MultiplyCumImage(pGuidedData, pData, &pGPCum);

    int field_size;
    UINT64 cum, square_cum;
    long uprow, downrow, upidx, downidx;        // In cumulative image
    long leftcol, rightcol;
    float g_mean, g_var;        // mean and variance of guided image
    long row_idx = 0;
    UINT64 p_cum, gp_cum;
    float p_mean;
    // Calculate a and b
    // Since we're going to calculate cumulative image of a and b, we have to calculate the whole image of a and b.
    for (i = 0; i < Height; i++)
    {
        // Check the boundary for radius
        if (i < radius) uprow = 0;
        else uprow = i - radius;
        upidx = uprow*(Width + 1);
        if (i + radius >= Height) downrow = Height;
        else downrow = i + radius + 1;
        downidx = downrow*(Width + 1);
        for (j = 0; j < Width; j++)
        {
            // Check the boundary for radius
            if (j < radius) leftcol = 0;
            else leftcol = j - radius;
            if (j + radius >= Width) rightcol = Width;
            else rightcol = j + radius + 1;
            field_size = (downrow - uprow)*(rightcol - leftcol);
            long p1, p2, p3, p4;
            p1 = downidx + rightcol;
            p2 = downidx + leftcol;
            p3 = upidx + rightcol;
            p4 = upidx + leftcol;
            // Guided image summary in the field
            cum = pCum[p1] - pCum[p2] - pCum[p3] + pCum[p4];
            // Guided image square summary in the field
            square_cum = pSquareCum[p1] - pSquareCum[p2] - pSquareCum[p3] + pSquareCum[p4];
            // Field mean
            g_mean = (float)(cum) / field_size;
            // Field variance
            g_var = float(square_cum) / field_size - g_mean * g_mean;
            // Summary of input image in the field
            p_cum = pInputCum[p1] - pInputCum[p2] - pInputCum[p3] + pInputCum[p4];
            // Input image field mean
            p_mean = float(p_cum) / field_size;
            // Multiply summary in the field
            gp_cum = pGPCum[p1] - pGPCum[p2] - pGPCum[p3] + pGPCum[p4];
            long idx = row_idx + j;
            pa[idx] = (float(gp_cum) / field_size - g_mean*p_mean) / (g_var + epsilon);
            pb[idx] = p_mean - g_mean*pa[idx];
        }
        row_idx += Width;
    }
    // not needed after this
    delete[] pCum;
    delete[] pSquareCum;
    delete[] pInputCum;
    delete[] pGPCum;

    // Cumulative image of a and b
    float *pCuma = NULL, *pCumb = NULL;
    CumImage(pa, &pCuma);
    CumImage(pb, &pCumb);

    // Finally calculate the output image q=ag+b
    float mean_a, mean_b;
    row_idx = Hstart*Width;
    for (i = Hstart; i < Hend; i++)
    {
        // Check the boundary for radius
        if (i < radius) uprow = 0;
        else uprow = i - radius;
        upidx = uprow*(Width + 1);
        if (i + radius >= Height) downrow = Height;
        else downrow = i + radius + 1;
        downidx = downrow*(Width + 1);
        for (j = Wstart; j < Wend; j++)
        {
            // Check the boundary for radius
            if (j < radius) leftcol = 0;
            else leftcol = j - radius;
            if (j + radius >= Width) rightcol = Width;
            else rightcol = j + radius + 1;
            field_size = (downrow - uprow)*(rightcol - leftcol);
            long p1, p2, p3, p4;
            p1 = downidx + rightcol;
            p2 = downidx + leftcol;
            p3 = upidx + rightcol;
            p4 = upidx + leftcol;
            // Field mean
            mean_a = (pCuma[p1] - pCuma[p2] - pCuma[p3] + pCuma[p4]) / field_size;
            // Field mean
            mean_b = (pCumb[p1] - pCumb[p2] - pCumb[p3] + pCumb[p4]) / field_size;
            // New pixel value
            long idx = row_idx + j;
            int value = int(mean_a*pGuidedData[idx] + mean_b);
            CLAMP0255(value);
            pData[idx] = value;
        }
        row_idx += Width;
    }

    delete[] pa;
    delete[] pb;
    delete[] pCuma;
    delete[] pCumb;

图片 33  (5)

View
Code

View
Code

图片 34  (6)

 导向滤波还有一种高效算法,基本考虑是透过下采样输入图P和指导图G,拿到较小的图像P’和G’。用它们来计量全面A’和B’。然后经过线性插值的法子苏醒原来大小得到近似的A和B,用来和原有大小的指导图来测算输出Q。那样,ak和bk的计量不是在全尺寸图像上,能省掉不计其数运算量,而最后的结果不受很大影响。

 导向滤波还有一种高效算法,基本思想是透过下采样输入图P和指点图G,得到较小的图像P’和G’。用它们来计算周密A’和B’。然后经过线性插值的方法恢复原来大小得到近似的A和B,用来和原来大小的率领图来计量输出Q。那样,ak和bk的盘算不是在全尺寸图像上,能节约成千成万运算量,而最后的结果不受很大影响。

其中图片 35是引导图G和输入图P在Wk的协方差。

 下边以含有保边平滑性子的导向滤波为例,来探视效果。如上一篇所说,当输入图P和率领图G相同时,导向滤波显示保边平滑天性。

 上边以带有保边平滑个性的导向滤波为例,来看望效果。如上一篇所说,当输入图P和指点图G相同时,导向滤波展现保边平滑特性。

计算出ak,bk后,就可以根据(1)来计量窗口Wk的出口像素。对于二个像素i,输出值qi和全部覆盖像素i的窗口Wk有关。所以当Wk不同,qi的值也不相同。三个简单易行的方针是平均全部大概的qi值。统计了具备覆盖i的窗口Wk的ak,bk,全部覆盖像素i的窗口Wk的个数为|W|,那么

图片 36  
图片 37

图片 38  
图片 39

图片 40  (7)

                              
原图                                                                                                
半径3,ε=52

                              
原图                                                                                                
半径3,ε=52

图片 41  (8)

图片 42  
图片 43

图片 44  
图片 45

其中图片 46图片 47

                   
半径3,ε=102                                                
                                          
半径3,ε=202

                   
半径3,ε=102                                                
                                          
半径3,ε=202


图片 48
  图片 49

图片 50
  图片 51

尤其的,当指导图G和输入图像P相同的时候,导向滤波出现边缘保持平滑特性,分析如下。当G=P时,很扎眼图片 52图片 53,由式(5)(6)得到图片 54图片 55。当ε=0时,ak=1,bk=0,即出口和输入图像相同。若是ε>0,考虑三种情况。

                         原图            
                                         
 半径5,ε=202

                         原图            
                                         
 半径5,ε=202

先是种,高方差。若是图像P在窗口Wk中有那些变更,那么σk2>>ε,有ak≈1,bk≈0。

 总体来说,P=G时,导向滤波的保边平滑性情和含有保边功用领域平滑滤波有接近的机能。

 总体来说,P=G时,导向滤波的保边平滑个性和富含保边效带领域平滑滤波有接近的职能。

第二种,平坦块。那么σk2<<ε,有ak≈0,bk≈μk。借使整个输入图像都如窗口Wk貌似很平整的,当ak,bk被平均后得到/ak≈0,/bk≈μk,qi≈μk

 

 

那样,当贰个像素在高方差的窗口中时,它的输出值是不变的。在平坦区域中,它的输出值变成周围窗口像素的平均值。具体的,高方差和平坦的正规化是由参数ε控制的。假如窗口的方差比此参数小的多则被平整,那么方差大得多的则被保存。而窗口的轻重缓急决定了是参考周围多大范围的像向来测算方差和均值。


迄今截止,已经可以根据(5)~(8)式统计导向滤波的参数,从而计算输出图像Q。导向滤波的的算法实以后下一篇小说介绍。上面把导向滤波用通用滤波器核来表明举办更为分析。

导向滤波在像素点i的滤波结果可以发挥为1个加权平均

图片 56  (9)

内部i,j都是像素下标。滤波器核Wij是辅导图G的函数并且与P独立。下边统计滤波器核。把(6)带入(8)消去b得到:

图片 57  (10)

求偏导

图片 58  (11)

其中

图片 59  (12)

图片 60当j处于窗口Wk时,否则为0。

图片 61

图片 62  (13)

把(12)(13)带入(11)得到

图片 63

图片 64  (14)

图片 65

考虑富含边缘阶跃信号的图像,如若i,j在边缘的同一侧,(gik)和(gjk)符号相同,而当它们在边缘的两侧时则符号差距。所以Wij(G)当八个像素点在边缘的两侧时比在平等边时要小得多。那就证实隔着边缘的时候,pj对结果的进献很小,窗口像素不会平均到一块。而当j和i在边缘的同侧时,输出像素qi是同侧像素的加权平均值。加权系数由导向图G决定。因此能够看出,导向滤波确实可以起到保留边缘的效益。

相关文章