我正在尝试了解超快速模糊算法背后的算法。下面是与Android一起测试的Java的端口。看起来这个版本具有一些我不太了解的优化,也没有任何评论。
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<=radius;i++){
p=pix[yi+Math.min(wm,Math.max(i,0))];
rsum+=(p & 0xff0000)>>16;
gsum+=(p & 0x00ff00)>>8;
bsum+= p & 0x0000ff;
}
for (x=0;x<w;x++){
r[yi]=dv[rsum];
g[yi]=dv[gsum];
b[yi]=dv[bsum];
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);
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<=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]];
接下来是让我最困惑的原因:我是否正确地说,正在获得左右像素之间的区别,并补充说我们拥有的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是滑动窗口内的像素的累积之和。您看到的是右侧的新像素被添加到总和中,最左边的像素n the窗口从总和中删除。
这个框模糊算法在2001年的本文中概述了。
基本上做的是模糊图像两次。首先是在水平方向上,然后在垂直方向上。最终结果与您已经用平方盒2r+1
像素(即从x-r
到x+r
)以及从每个点的y-r
到y+r
计算了图像的卷积相同。
在每个步骤中,模糊的像素值只是该范围内所有像素的平均值。可以通过在每个点保持总数来快速计算。当您向右移动范围(向下)一个像素时,您会在左侧(顶部)端减去像素,然后在右侧(底部)端添加像素。您仍然必须将这些运行总计除以2r+1
,但是可以通过对(0≤n<256)
的n/(2r+1)
的定点值进行预先计算,并将它们存储在dv[]
中(带有8位分数零件)。
每次扫描开始时的简短求和循环就在那里计算运行总数的初始值。
以及与max()
和min()
的一些杂耍,以避免访问范围的像素,这就是所有内容。
使用compoundblur
提示您会从梯度表中注意到模糊将从外部向内构建,因此它会首先模糊边缘,然后模糊中心。为了从中心向边缘模糊,只需在> mul_table 中取下所有值,然后从中减去255:这会反转位图 - 您可以想象渐变图中像素的亮度等于在那里使用的模糊半径 - 白色像素大模糊,黑色像素小模糊。
快速反转的方法:
使用Sublime Text和Microsoft Excel,您可以轻松地倒转值...
崇高的文字:
将所有值都垂直列出,然后用鼠标轮键单击和拖动,您可以向下选择并击中Enter以将单个数字放在一条线上。现在,再次使用鼠标轮单击并拖动,然后在每个值之后插入" - 255",然后在每个值之前插入" ="(也单击并拖动以选择所有逗号并删除它们)。现在选择所有行并复制。
Excel的最终格式应为: = (原始mul_table值) -255 ... I.E. = = 512 -255555555555555
excel:在崇高复制格式的值之后,粘贴到excel中最左上角的小区,Excel将为您评估" = 512-255",并立即创建新的反转值。复制所有单元格并将其粘贴到JS文件中,然后插入逗号。
现在,您的复合布鲁尔将从中心向边缘模糊。