基于H5canvas和js的高斯模糊处理

效果

先上效果图
这里写图片描述

原理

高斯模糊的原理中,它是根据高斯曲线调节像素色值,它是有选择地模糊图像。说得直白一点,就是高斯模糊能够把某一点周围的像素色值按高斯曲线统计起来,采用数学上加权平均的计算方法得到这条曲线的色值,最后能够留下人物的轮廓,即曲线. 更加具体的解释请移步到 阮一峰的高斯模糊算法.
本篇blog主要讨论js的高斯函数及其应用.

采用了两种分别不同的函数来处里像素位点, 第一种是直接使用二种循环, 时间复杂度为O(xy(2r)^2). 另一种是采用一维公式分别对x, y进行循环处理, 时间复杂度为O(2xy(2r)).

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<img src="林徽因.jpg" id="imgSource">
<canvas id="canvas"></canvas>
<script>
window.onload = function(){
var img = document.getElementById("imgSource"),
canvas = document.getElementById('canvas'),
width = img.width,
height = img.height;
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);
var tempData = gaussBlur1(canvasData, 20);
context.putImageData(tempData,0,0);
}
/**
* 此函数为二重循环
*/
function gaussBlur(imgData, radius, sigma) {
var pixes = imgData.data,
width = imgData.width,
height = imgData.height;
radius = radius || 5;
sigma = sigma || radius / 3;
var gaussEdge = radius * 2 + 1; // 高斯矩阵的边长
var gaussMatrix = [],
gaussSum = 0,
a = 1 / (2 * sigma * sigma * Math.PI),
b = -a * Math.PI;
for (var i=-radius; i<=radius; i++) {
for (var j=-radius; j<=radius; j++) {
var gxy = a * Math.exp((i * i + j * j) * b);
gaussMatrix.push(gxy);
gaussSum += gxy; // 得到高斯矩阵的和,用来归一化
}
}
var gaussNum = (radius + 1) * (radius + 1);
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);
// 计算每个点的高斯处理之后的值
for (var i=-radius; i<=radius; i++) {
// 处理边缘
var m = handleEdge(i, x, width);
for (var j=-radius; j<=radius; j++) {
// 处理边缘
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;
}
/**
* 此函数为分别循环
*/
function gaussBlur1(imgData,radius, sigma) {
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;
radius = Math.floor(radius) || 3;
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;
for(j = -radius; j <= radius; j++){
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];
gaussSum += gaussMatrix[j + radius];
}
}
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;
for(j = -radius; j <= radius; j++){
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];
gaussSum += gaussMatrix[j + radius];
}
}
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;
}
</script>
</body>
</html>
越来越多的平台(微信公众平台,新浪微博,简书,百度打赏等)支持打赏功能,付费阅读时代越来越近,特此增加了打赏功能,支持微信打赏和支付宝打赏。坚持原创技术分享,您的支持将鼓励我继续创作!