js高斯模糊算法问题

LingYan
• 220
``````<!DOCTYPE html>
<html lang="en">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<title>高斯模糊</title>
<body>
<div id="wrap" class="wrap">
<img src="img/12338563.jpg" alt="" id="imageSource" height="500" width="800"/>
<canvas id="myCanvas"></canvas>
</div>
<script type="text/javascript">
//-------------------------------做一个基于canvas的【图片上传，剪切】的插件
//---------------------------------测试需在服务器环境下
var canvas = document.getElementById("myCanvas");
var image = document.getElementById("imageSource");
// re-size the canvas deminsion
console.log(image.width)
canvas.width = image.width;
canvas.height = image.height;
// get 2D render object
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0,canvas.width,canvas.height);
var canvasData = context.getImageData(0, 0, canvas.width, canvas.height); //获取图片信息

//对于canvas来说，确定【width，height】就确定了【canvasData.data】的长度为【width*height*4】
//但是坐标范围用width,height来固定

//-------------------------------------------------高斯模糊开始------------------------------------------------------
var radius = 20 ; //定义模糊半径
var quanSub = ArrSub(quan);
quan = jiaQuan(quan,quanSub); //获取权值

var canvasDataCtrl = canvasData ;
var width = canvas.width ,
height = canvas.height ;
console.log(new Date().getSeconds()) //高斯模糊开始时间
for (var i=0;i<width ;i++ )
{
for (var j=0;j<height ;j++ )
{
var index= (width*4)*j+i*4;
canvasDataCtrl.data[index] = getValue(i,j,0,quan); //r
canvasDataCtrl.data[index+1] = getValue(i,j,1,quan); //g
canvasDataCtrl.data[index+2] = getValue(i,j,2,quan); //b
canvasDataCtrl.data[index+3] = getValue(i,j,3,quan); //a
}
}
context.putImageData(canvasDataCtrl, 0, 0);

console.log(new Date().getSeconds()) //高斯模糊结束时间
//console.log(value)
//在chrome上运行时间大概有30s，运行这么长时间，肯定是算法问题，网上的StackBlur运行时间就很短
//在计算量上，如果图片是像素是500×800,那这张图片所包含的rgba信息就有160,0000 之多
//再加上要对每个像素的rgba值进行高斯转换，计算量就相当之大了，怎么解决呢
console.log('模糊完成')

function gaosi(x,y,a){ //根据高斯二维公式，获取点的高斯值
var e = Math.E ;
var pi = Math.PI ;
return 1/(2*pi*a*a)*Math.pow(e,-((x*x+y*y)/(2*a*a)))
}
//数组第一个元素的坐标是【-x,x】

var quan = []
{
{
quan.push(gaosi(i,j,20));
}
}
return quan ;
}
function ArrSub(arr){ //返回高斯数组所有元素的值的和
var sub = 0 ;
for (var i=0,len=quan.length;i<len ;i++ )
{
sub += quan[i] ;
}
return sub ;
}
function jiaQuan(arr,quanSub){ //获取权的数组
for (var i=0,len=arr.length;i<len ;i++ )
{
arr[i] = arr[i]/quanSub;
}
return arr ;
}
function getValue(x,y,index,quan){ //根据坐标以及rgba[index]来算出高斯模糊的最终值
var imgdata = getInfoArr(x,y,index)
var value = 0 ;
for (var i=0,len=imgdata.length;i<len ;i++ )
{
value += imgdata[i]*quan[i] ;
}
return Math.round(value) ;//返回整数
}

function getInfo(x,y,index){ //返回点的【rgba】值
if (x<0)
{
x=-x
}else if (x>width-1)
{
x=2*(width-1)-x
}
if (y<0)
{
y=-y
}else if (y>height-1)
{
y=2*(height-1)-y
}
var i = (width*4)*y+x*4+index;
return canvasData.data[i]
}
var arr = [];
{
{
arr.push(getInfo(i,j,index))
}
}
return arr ;
}
};

</script>
</body>
</html>
``````

3 个回答
mapleeit
• 31
✓ 已被采纳

• 直接用二维公式进行二重循环，复杂度为O(xy(2r)^2)
• 用一维公式分别对x、y循环，复杂度为O(2xy(2r))

• 用二重循环：（500*800,20） 4566ms
• 分别循环：（500*800,20） 237ms

实现代码如下：

html:

````html````<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<title>test</title>
<script src="GaussianBlur.js"></script>
<body>
<img src="images/test3.jpg" alt="img source" id="imgSource">
<canvas id="canvas"></canvas>
</body>
</html>
``````

javascript:

• gaussBlur : 二重循环
• gaussBlur1 : 分别循环
````javascript````/**
* Created by zhaofengmiao on 15/3/22.
*/
var img = document.getElementById("imgSource"),
canvas = document.getElementById('canvas'),
width = img.width,
height = img.height;

// console.log(width);

canvas.width = width;
canvas.height = height;

var context = canvas.getContext("2d");
context.drawImage(img, 0, 0);

var canvasData = context.getImageData(0, 0, canvas.width, canvas.height);

//console.log(canvasData);

// 开始
var startTime = +new Date();

var tempData = gaussBlur(canvasData, 20);

context.putImageData(tempData,0,0);

var endTime = +new Date();
console.log(" 一共经历时间：" + (endTime - startTime) + "ms");
}

/**
* 此函数为二重循环
*/
var pixes = imgData.data,
width = imgData.width,
height = imgData.height;

sigma = sigma || radius / 3;

var gaussEdge = radius * 2 + 1;    // 高斯矩阵的边长

var gaussMatrix = [],
gaussSum = 0,
a = 1 / (2 * sigma * sigma * Math.PI),
b = -a * Math.PI;

var gxy = a * Math.exp((i * i + j * j) * b);
gaussMatrix.push(gxy);
gaussSum += gxy;    // 得到高斯矩阵的和，用来归一化
}
}
for (var i=0; i<gaussNum; i++) {
gaussMatrix[i] = gaussMatrix[i] / gaussSum;    // 除gaussSum是归一化
}

//console.log(gaussMatrix);

// 循环计算整个图像每个像素高斯处理之后的值
for (var x=0; x<width;x++) {
for (var y=0; y<height; y++) {
var r = 0,
g = 0,
b = 0;

//console.log(1);

// 计算每个点的高斯处理之后的值
// 处理边缘
var m = handleEdge(i, x, width);
// 处理边缘
var mm = handleEdge(j, y, height);

var currentPixId = (mm * width + m) * 4;

var jj = j + radius;
var ii = i + radius;
r += pixes[currentPixId] * gaussMatrix[jj * gaussEdge + ii];
g += pixes[currentPixId + 1] * gaussMatrix[jj * gaussEdge + ii];
b += pixes[currentPixId + 2] * gaussMatrix[jj * gaussEdge + ii];

}
}
var pixId = (y * width + x) * 4;

pixes[pixId] = ~~r;
pixes[pixId + 1] = ~~g;
pixes[pixId + 2] = ~~b;
}
}
imgData.data = pixes;
return imgData;
}

function handleEdge(i, x, w) {
var  m = x + i;
if (m < 0) {
m = -m;
} else if (m >= w) {
m = w + i - x;
}
return m;
}

/**
* 此函数为分别循环
*/
var pixes = imgData.data;
var width = imgData.width;
var height = imgData.height;
var gaussMatrix = [],
gaussSum = 0,
x, y,
r, g, b, a,
i, j, k, len;

sigma = sigma || radius / 3;

a = 1 / (Math.sqrt(2 * Math.PI) * sigma);
b = -1 / (2 * sigma * sigma);
//生成高斯矩阵
for (i = 0, x = -radius; x <= radius; x++, i++){
g = a * Math.exp(b * x * x);
gaussMatrix[i] = g;
gaussSum += g;

}
//归一化, 保证高斯矩阵的值在[0,1]之间
for (i = 0, len = gaussMatrix.length; i < len; i++) {
gaussMatrix[i] /= gaussSum;
}
//x 方向一维高斯运算
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
r = g = b = a = 0;
gaussSum = 0;
k = x + j;
if(k >= 0 && k < width){//确保 k 没超出 x 的范围
//r,g,b,a 四个一组
i = (y * width + k) * 4;
r += pixes[i] * gaussMatrix[j + radius];
g += pixes[i + 1] * gaussMatrix[j + radius];
b += pixes[i + 2] * gaussMatrix[j + radius];
// a += pixes[i + 3] * gaussMatrix[j];
}
}
i = (y * width + x) * 4;
// 除以 gaussSum 是为了消除处于边缘的像素, 高斯运算不足的问题
// console.log(gaussSum)
pixes[i] = r / gaussSum;
pixes[i + 1] = g / gaussSum;
pixes[i + 2] = b / gaussSum;
// pixes[i + 3] = a ;
}
}
//y 方向一维高斯运算
for (x = 0; x < width; x++) {
for (y = 0; y < height; y++) {
r = g = b = a = 0;
gaussSum = 0;
k = y + j;
if(k >= 0 && k < height){//确保 k 没超出 y 的范围
i = (k * width + x) * 4;
r += pixes[i] * gaussMatrix[j + radius];
g += pixes[i + 1] * gaussMatrix[j + radius];
b += pixes[i + 2] * gaussMatrix[j + radius];
// a += pixes[i + 3] * gaussMatrix[j];
}
}
i = (y * width + x) * 4;
pixes[i] = r / gaussSum;
pixes[i + 1] = g / gaussSum;
pixes[i + 2] = b / gaussSum;
// pixes[i] = r ;
// pixes[i + 1] = g ;
// pixes[i + 2] = b ;
// pixes[i + 3] = a ;
}
}
//end
imgData.data = pixes;
return imgData;
}
``````
sunway
• 4
mearo
• 1

你尚未登录，登录后可以
• 和开发者交流问题的细节
• 关注并接收问题和回答的更新提醒
• 参与内容的编辑和改进，让解决方法与时俱进