快速高斯模糊(线性复杂度)
来源:互联网 发布:人工智能 黑箱 编辑:程序博客网 时间:2024/04/29 22:58
Fastest Gaussian Blur (in linear time)
I needed really fast Gaussian blur for one of my projects. After hours of struggling and browsing the internet, I finally found the best solution.
Beginning
My solution is based on Fast image convolutions by Wojciech Jarosz. Presented ideas are very simple and I don't know who is the original author. I am going to describe it a little better and add some mathematics. To get motivated, take a glance at the results. I have implemented this code into Photopea under Filter - Blur - Gaussian Blur.
Definition
The convolution of two 2D functions
The Gaussian blur of a 2D function can be defined as a convolution of that function with 2DGaussian function. Our gaussian function has an integral 1 (volume under surface) and is uniquely defined by one parameter
In our discrete finite case, we represent our 2D functions as matrices of values. We compute the volume (integral) as a sum. Gaussian function has near to zero values behind some radius, so we will use only the values
Algorithm 1
For a general discrete convolution of
For gaussian weight, we can compute only weights around [i, j] (area of
1// source channel, target channel, width, height, radius2function gaussBlur_1 (scl, tcl, w, h, r) {3 var rs = Math.ceil(r * 2.57); // significant radius4 for(var i=0; i<h; i++)5 for(var j=0; j<w; j++) {6 var val = 0, wsum = 0;7 for(var iy = i-rs; iy<i+rs+1; iy++)8 for(var ix = j-rs; ix<j+rs+1; ix++) {9 var x = Math.min(w-1, Math.max(0, ix));10 var y = Math.min(h-1, Math.max(0, iy));11 var dsq = (ix-j)*(ix-j)+(iy-i)*(iy-i);12 var wght = Math.exp( -dsq / (2*r*r) ) / (Math.PI*2*r*r);13 val += scl[y*w+x] * wght; wsum += wght;14 }15 tcl[i*w+j] = Math.round(val/wsum); 16 }17}
Algorithm 2
Let's introduce the box blur. It is the convolution of function
In this algorithm, we will simulate the gaussian blur with 3 passes of box blur. Let's denote the half of size of square as
We have to convert the standard deviation of gaussian blur
1function boxesForGauss(sigma, n) // standard deviation, number of boxes2{3 var wIdeal = Math.sqrt((12*sigma*sigma/n)+1); // Ideal averaging filter width 4 var wl = Math.floor(wIdeal); if(wl%2==0) wl--;5 var wu = wl+2;67 var mIdeal = (12*sigma*sigma - n*wl*wl - 4*n*wl - 3*n)/(-4*wl - 4);8 var m = Math.round(mIdeal);9 // var sigmaActual = Math.sqrt( (m*wl*wl + (n-m)*wu*wu - n)/12 );1011 var sizes = []; for(var i=0; i<n; i++) sizes.push(i<m?wl:wu);12 return sizes;13}
Our algorithm has still the same complexity
1function gaussBlur_2 (scl, tcl, w, h, r) {2 var bxs = boxesForGauss(r, 3);3 boxBlur_2 (scl, tcl, w, h, (bxs[0]-1)/2);4 boxBlur_2 (tcl, scl, w, h, (bxs[1]-1)/2);5 boxBlur_2 (scl, tcl, w, h, (bxs[2]-1)/2);6}7function boxBlur_2 (scl, tcl, w, h, r) {8 for(var i=0; i<h; i++)9 for(var j=0; j<w; j++) {10 var val = 0;11 for(var iy=i-r; iy<i+r+1; iy++)12 for(var ix=j-r; ix<j+r+1; ix++) {13 var x = Math.min(w-1, Math.max(0, ix));14 var y = Math.min(h-1, Math.max(0, iy));15 val += scl[y*w+x];16 }17 tcl[i*w+j] = val/((r+r+1)*(r+r+1));18 }19}
Algorithm 3
We have already simplified gaussian blur into 3 passes of box blur. Let's do a little experiment. Let's define a horizontal blur and total blur:
Those two functions are "looping" in a line, producing "one-dimensional blur". Let's see, what total blur corresponds to:
We just discovered, that our total blur is box blur! Both total blur and horizontal blur have a complexity
1function gaussBlur_3 (scl, tcl, w, h, r) {2 var bxs = boxesForGauss(r, 3);3 boxBlur_3 (scl, tcl, w, h, (bxs[0]-1)/2);4 boxBlur_3 (tcl, scl, w, h, (bxs[1]-1)/2);5 boxBlur_3 (scl, tcl, w, h, (bxs[2]-1)/2);6}7function boxBlur_3 (scl, tcl, w, h, r) {8 for(var i=0; i<scl.length; i++) tcl[i] = scl[i];9 boxBlurH_3(tcl, scl, w, h, r);10 boxBlurT_3(scl, tcl, w, h, r);11}12function boxBlurH_3 (scl, tcl, w, h, r) {13 for(var i=0; i<h; i++)14 for(var j=0; j<w; j++) {15 var val = 0;16 for(var ix=j-r; ix<j+r+1; ix++) {17 var x = Math.min(w-1, Math.max(0, ix));18 val += scl[i*w+x];19 }20 tcl[i*w+j] = val/(r+r+1);21 }22} 23function boxBlurT_3 (scl, tcl, w, h, r) {24 for(var i=0; i<h; i++)25 for(var j=0; j<w; j++) {26 var val = 0;27 for(var iy=i-r; iy<i+r+1; iy++) {28 var y = Math.min(h-1, Math.max(0, iy));29 val += scl[y*w+j];30 }31 tcl[i*w+j] = val/(r+r+1);32 }33}
Algorithm 4
One-dimensional blur can be computed even faster. E. g. we want to compute horizontal blur. We compute
In our new algorithm, we will compute the one-dimensional blur by creating the accumulator. First, we put the value of left-most cell into it. Then we will compute next values just by editing the previous value in constant time. This 1D blur has the complexity
1function gaussBlur_4 (scl, tcl, w, h, r) {2 var bxs = boxesForGauss(r, 3);3 boxBlur_4 (scl, tcl, w, h, (bxs[0]-1)/2);4 boxBlur_4 (tcl, scl, w, h, (bxs[1]-1)/2);5 boxBlur_4 (scl, tcl, w, h, (bxs[2]-1)/2);6}7function boxBlur_4 (scl, tcl, w, h, r) {8 for(var i=0; i<scl.length; i++) tcl[i] = scl[i];9 boxBlurH_4(tcl, scl, w, h, r);10 boxBlurT_4(scl, tcl, w, h, r);11}12function boxBlurH_4 (scl, tcl, w, h, r) {13 var iarr = 1 / (r+r+1);14 for(var i=0; i<h; i++) {15 var ti = i*w, li = ti, ri = ti+r;16 var fv = scl[ti], lv = scl[ti+w-1], val = (r+1)*fv;17 for(var j=0; j<r; j++) val += scl[ti+j];18 for(var j=0 ; j<=r ; j++) { val += scl[ri++] - fv ; tcl[ti++] = Math.round(val*iarr); }19 for(var j=r+1; j<w-r; j++) { val += scl[ri++] - scl[li++]; tcl[ti++] = Math.round(val*iarr); }20 for(var j=w-r; j<w ; j++) { val += lv - scl[li++]; tcl[ti++] = Math.round(val*iarr); }21 }22}23function boxBlurT_4 (scl, tcl, w, h, r) {24 var iarr = 1 / (r+r+1);25 for(var i=0; i<w; i++) {26 var ti = i, li = ti, ri = ti+r*w;27 var fv = scl[ti], lv = scl[ti+w*(h-1)], val = (r+1)*fv;28 for(var j=0; j<r; j++) val += scl[ti+j*w];29 for(var j=0 ; j<=r ; j++) { val += scl[ri] - fv ; tcl[ti] = Math.round(val*iarr); ri+=w; ti+=w; }30 for(var j=r+1; j<h-r; j++) { val += scl[ri] - scl[li]; tcl[ti] = Math.round(val*iarr); li+=w; ri+=w; ti+=w; }31 for(var j=h-r; j<h ; j++) { val += lv - scl[li]; tcl[ti] = Math.round(val*iarr); li+=w; ti+=w; }32 }33}
Results
I was testing all 4 algorithms on an image below (4 channels, 800x200 pixels). Here are the results:
Note, that Alg 1 is computing the true Gaussian blur using gaussian kernel, while Alg 2,3,4 are only approximating it with 3 passes of box blur. The difference between Alg 2,3,4 is in complexity of computing box blur, their outputs are the same.
Original image
Perfect Gaussian Blur (Algorithm 1)
Algorithm 2, 3, 4, average error per pixel: 0.04 %
Blur by Adobe Photoshop, average error per pixel: 0.09%
- 快速高斯模糊(线性复杂度)
- 快速高斯模糊(IIR递归高斯模糊)
- 快速高斯模糊
- 快速高斯模糊
- 快速高斯模糊算法
- 快速高斯模糊算法
- Android 实现快速高斯模糊(毛玻璃)效果算法
- 快速高斯模糊[剪裁版]
- Android快速高斯模糊对话框
- 【快速高斯模糊的实现】
- Android实现快速高斯模糊
- 使用递归高斯滤波器实现快速高斯模糊
- 使用递归高斯滤波器实现快速高斯模糊
- 最快的高斯模糊(线性时间)Fastest Gaussian Blur (in linear time)
- Android 毛玻璃(高斯模糊) 开源控件。简单、快速、高效。(基于fastblur)
- 最快速的“高斯”模糊算法(附Android源码)
- 快速高斯模糊 实现及相干题目
- 利用RenderScript对图像快速高斯模糊(一)
- 操作系统学习笔记(4)——进程的控制与调度
- 自定义Command
- 积分入学数据库设计问题汇总
- 微信端口及协议分析
- 学习:WordXML格式初步分析
- 快速高斯模糊(线性复杂度)
- 重装系统,升级Xcode7之后,发现安装不了CocoaPods
- hdu 5504 GT and sequence【BestCoder Round #60 】
- Android 蓝牙低能耗(BLE)
- android GTS-- com.google.android.xts.placement.UiPlacementTest fail
- Java中获取路径的各种方法
- 产品经理常用工具
- 网卡驱动程序
- java客户端和vc程序通信时的编码问题