了解超快速模糊算法

我试图理解超快速模糊算法背后的算法 。 下面是java的端口,它与android一起作为测试。 看起来这个版本做了一些我不太了解的优化,也没有任何评论。

void fastblur(Bitmap img, int radius){ if (radius<1){ return; } int w= img.getWidth(); int h=img.getHeight(); int wm=w-1; int hm=h-1; int wh=w*h; int div=radius+radius+1; int r[]=new int[wh]; int g[]=new int[wh]; int b[]=new int[wh]; int rsum,gsum,bsum,x,y,i,p,p1,p2,yp,yi,yw; int vmin[] = new int[Math.max(w,h)]; int vmax[] = new int[Math.max(w,h)]; int[] pix= new int[w*h]; img.getPixels(pix, 0, w, 0,0,w, h); int dv[]=new int[256*div]; for (i=0;i<256*div;i++){ dv[i]=(i/div); } yw=yi=0; for (y=0;y<h;y++){ rsum=gsum=bsum=0; for(i=-radius;i>16; gsum+=(p & 0x00ff00)>>8; bsum+= p & 0x0000ff; } for (x=0;x>16; gsum+=((p1 & 0x00ff00)-(p2 & 0x00ff00))>>8; bsum+= (p1 & 0x0000ff)-(p2 & 0x0000ff); yi++; } yw+=w; } for (x=0;x<w;x++){ rsum=gsum=bsum=0; yp=-radius*w; for(i=-radius;i<=radius;i++){ yi=Math.max(0,yp)+x; rsum+=r[yi]; gsum+=g[yi]; bsum+=b[yi]; yp+=w; } yi=x; for (y=0;y<h;y++){ pix[yi]=0xff000000 | (dv[rsum]<<16) | (dv[gsum]<<8) | dv[bsum]; if(x==0){ vmin[y]=Math.min(y+radius+1,hm)*w; vmax[y]=Math.max(y-radius,0)*w; } p1=x+vmin[y]; p2=x+vmax[y]; rsum+=r[p1]-r[p2]; gsum+=g[p1]-g[p2]; bsum+=b[p1]-b[p2]; yi+=w; } } img.setPixels(pix,0, w,0,0,w,h); } 

如果我的猜测错了,请纠正我:

以下循环有什么作用? 它与预先计算内核表有关吗? 怎么样的div,是内核表的大小? 我想我想问的是,什么是dv []应该存储?

 int dv[]=new int[256*div]; for (i=0;i<256*div;i++){ dv[i]=(i/div); } 

查看水平传递:下面的循环看起来总结了单独的RGB值,但它只在每行的起始像素处执行此操作,因为只有在我们完成处理所有像素直到达到宽度时才会增加yi 。 这是因为我们在处理下一个循环中的像素时最终会增加RGB总和吗?

  for(i=-radius;i>16; gsum+=(p & 0x00ff00)>>8; bsum+= p & 0x0000ff; } 

我们只根据半径和当前像素位置选择最左边的像素和最右边的像素吗?

  if(y==0){ vmin[x]=Math.min(x+radius+1,wm); vmax[x]=Math.max(x-radius,0); } p1=pix[yw+vmin[x]]; p2=pix[yw+vmax[x]]; 

接下来是让我最困惑的是:我是否正确地说,正确的左右像素之间的差异,并添加我们拥有的运行RGB总数?

  rsum+=((p1 & 0xff0000)-(p2 & 0xff0000))>>16; gsum+=((p1 & 0x00ff00)-(p2 & 0x00ff00))>>8; bsum+= (p1 & 0x0000ff)-(p2 & 0x0000ff); 

我没有看过第二遍,因为这几乎是我的头脑。 任何澄清将不胜感激,任何关于垂直通道循环的评论也会有所帮助,谢谢。

自从我写了一篇,我想我可以解释得最好:-)

  int dv[]=new int[256*div]; for (i=0;i<256*div;i++){ dv[i]=(i/div); } 

此行预先计算查找表,以查找可能发生的所有可能的平均值。 这是为了避免在内环中进行昂贵的划分。 在某些系统上直接进行除法而不是进行数组查找现在实际上可能更快,但是当我写它时,查找是更快的方式。

 for(i=-radius;i<=radius;i++){ int ind = yi+Math.min(wm,Math.max(i,0)); p=pix[ind]; rsum+=(p & 0xff0000)>>16; gsum+=(p & 0x00ff00)>>8; bsum+= p & 0x0000ff; } 

该算法快速的原因是它使用滑动窗口,因此减少了所需的像素查找次数。 窗口从左边缘向右边滑动(在第二次从顶部到底部),只在右边添加一个像素,从左边移除一个像素。 上面的代码通过使用最左边的像素预填充窗口来初始化窗口,具体取决于内核大小。

  if(y==0){ vmin[x]=Math.min(x+radius+1,wm); vmax[x]=Math.max(x-radius,0); } p1=pix[yw+vmin[x]]; p2=pix[yw+vmax[x]]; 

这是添加新像素但同时处理边框条件的部分(当窗口尝试读取或删除位图外的像素时)。

  rsum+=((p1 & 0xff0000)-(p2 & 0xff0000))>>16; gsum+=((p1 & 0x00ff00)-(p2 & 0x00ff00))>>8; bsum+= (p1 & 0x0000ff)-(p2 & 0x0000ff); 

rsum,gsum和bsum是滑动窗口内的累积像素总和。 你看到的是右侧的新像素被添加到总和中,最左边的像素被从总和中移除。

从2001年开始 , 本文概述了这种盒子模糊算法。

它基本上做的是模糊图像两次; 首先是水平方向,然后是垂直方向。 最终结果与使用2r+1像素的方框计算图像的卷积相同(即,从xrx+r ,并且从每个点的yry+r )。

在每个步骤中,模糊像素值仅仅是该范围内所有像素的平均值。 这可以通过在每个点保持运行总计来快速计算。 将范围向右(向下)移动一个像素时,将减去左(顶)端的像素,并在右(底)端添加像素。 你仍然需要将这些运行总计除以2r+1 ,但这可以通过预先计算n/(2r+1)的定点值(0≤n<256)并将它们存储在dv[]来加速(一个8位小数部分)。

每次扫描开始时的短求和循环仅用于计算运行总计的初始值。

并且通过使用max()min()来避免访问超出范围的像素,这就是它的全部内容。

使用CompoundBlur时的提示

您将从渐变表中注意到模糊将从外部向内构建,因此它将首先模糊边缘然后模糊中心。 为了从中心向边缘模糊,只需获取mul_table中的所有值并从中减去255:这会反转位图 – 您可以想象渐变映射中像素的亮度等效于此处使用的模糊半径 -白色像素大模糊,黑色像素小模糊。

快速反转的方法:

使用Sublime Text和Microsoft Excel,您可以轻松地反转值…

崇高文字:

将所有值放入列中,使用逗号垂直排列,然后通过单击并拖动鼠标滚轮,可以向下选择并按Enter键将单个数字放在一行上。 现在再次单击并拖动鼠标滚轮并在每个值后插入“ – 255”,并在每个值前面加上“=”(同时单击并拖动以选择所有逗号并删除它们)。 现在选择所有行并复制。

Excel的最终格式应为: = (原始mul_table值) – 255 … ie = 512 – 255

Excel:在Sublime中复制格式化值后,粘贴到Excel中最左上角的单元格,Excel将为您评估“= 512-255”并立即创建新的反转值。 复制所有单元格并粘贴回您的js文件并重新插入逗号。

你的CompoundBlur现在会从中心向边缘模糊。