[canvas入坑2]模糊效果
查看效果请到 http://philippica.github.io/ 点击blur
模糊效果比较好的应该是高斯模糊,一个点的值变成了以该点为圆心的一个圆内所有像素的加权平均,权重由二维正态分布计算出
以前用qt实现过一个高斯模糊,但是js的速度毕竟不能和c++比,为了实现一个比较好的效果,这里在效率和效果方面做了个折中,每一点的值等于以这个点为中心的正方形内所有像素值的平均
这样的优点是计算速度快,矩阵天然的存着正方形,所以通过一个O(canvasWidth * canvasHeight)的预处理,接下来即可迅速地得到每个像素的值
具体方法应该是acm里用烂的方法,sum[i][j]存着以(i,j)为右下角的到左上角画布中所有该通道值的和,于是画布中任意矩形的和都可以用一个类似容斥的方法O(1)得到
预处理的代码:
1 var ppImgData = context.getImageData(0, 0, ppCanvasWidth, ppCanvasHeight); 2 var ppData = ppImgData.data; 3 var ppTemp = ppData; 4 var radius = 0; 5 var length = ppData.length; 6 $('#range').attr("value", 0); 7 for(var i = 0; i < ppCanvasHeight; i++) 8 { 9 for(var j = 0; j < ppCanvasWidth; j++) 10 { 11 var position = i * ppCanvasWidth + j; 12 var x = (position) * 4; 13 sumR[position] = ppData[x]; 14 sumG[position] = ppData[x + 1]; 15 sumB[position] = ppData[x + 2]; 16 if(i != 0) 17 { 18 sumR[position] += sumR[position - ppCanvasWidth]; 19 sumG[position] += sumG[position - ppCanvasWidth]; 20 sumB[position] += sumB[position - ppCanvasWidth]; 21 } 22 if(j != 0) 23 { 24 sumR[position] += sumR[position - 1]; 25 sumG[position] += sumG[position - 1]; 26 sumB[position] += sumB[position - 1]; 27 } 28 if(i != 0 && j != 0) 29 { 30 sumR[position] -= sumR[position - ppCanvasWidth - 1]; 31 sumG[position] -= sumG[position - ppCanvasWidth - 1]; 32 sumB[position] -= sumB[position - ppCanvasWidth - 1]; 33 } 34 } 35 }
sumR sumG sumB分别存着三个通道内的预处理的值,radius表示模糊半径
具体计算模糊的代码:
1 function ppBlur() 2 { 3 var area = (radius + radius + 1) * (radius + radius + 1); 4 for(var i = 0; i < ppCanvasHeight; i++) 5 { 6 for(var j = 0; j < ppCanvasWidth; j++) 7 { 8 var position = i * ppCanvasWidth + j; 9 var x = (position) * 4; 10 var tt = (i + radius) * ppCanvasWidth + j + radius; 11 var tt2 = (i - radius - 1) * ppCanvasWidth + j + radius; 12 var tt3 = (i + radius) * ppCanvasWidth + j - radius - 1; 13 var tt4 = (i - radius - 1) * ppCanvasWidth + j - radius - 1; 14 ppTemp[x] = (sumR[tt] - sumR[tt2] - sumR[tt3] + sumR[tt4]) / area; 15 ppTemp[x + 1] = (sumG[tt] - sumG[tt2] - sumG[tt3] + sumG[tt4]) / area; 16 ppTemp[x + 2] = (sumB[tt] - sumB[tt2] - sumB[tt3] + sumB[tt4]) / area; 17 } 18 } 19 ppData = ppTemp; 20 context.putImageData(ppImgData, 0, 0); 21 }
sumR[tt] - sumR[tt2] - sumR[tt3] + sumR[tt4]就是容斥出该正方形内R通道的和,再除以该区域内像素的总数即是平均值
总结:边界处理不好,在图片的边缘会明显变暗,主要是在边界处area的发生了变化