1 Star 0 Fork 6

zhizou/图像局部高斯模糊

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
localBlur.js 21.45 KB
一键复制 编辑 原始数据 按行查看 历史
张鑫旭 提交于 2021-09-30 21:37 . use natural size
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
/*!
* @description 指定图像在特定范围内高斯模糊,并且模糊的边缘是柔化的,因为需要兼容 IE,所以没有走 Promise
* 更多内容访问:https://www.zhangxinxu.com/wordpress/?p=10131
* @author zhangxinxu(.com)
* @create 2021-09-30
* @license MIT
*/
/**
* @param {String|Element} source 原始图像资源,可以是 <IMG>|<canvas> DOM 元素,也可以是 URL 字符串
* @param {Number} blur 模糊的半径大小
* @param {Array} bounding 模糊的半径位置,可选,如果不设置,则认为是整体模糊
* @param {Element|Function} output 显示模糊图像的 canvas 元素,或者是回调函数(参数是canvas)
* @returns 模糊后的 canvas 元素
*/
var localBlur = function (source, radius, bounding, output) {
// 获取图像资源
var eleSource = source;
if (typeof source == 'string') {
eleSource = document.createElement('img');
eleSource.onload = funDrawBlur;
eleSource.src = source;
}
if (!eleSource) {
return;
}
// 参数的识别
var numRadius = Number(radius);
var arrBounding = bounding;
var eleOutput = output;
var funOutput = output;
[radius, bounding, output].forEach(function (param) {
if (typeof param == 'number') {
numRadius = param;
} else if (typeof param == 'function') {
funOutput = param;
} else if (param && param.map) {
arrBounding = param;
} else if (param && param.getContext) {
eleOutput = param;
}
});
// 创建 canvas 元素
if (!eleOutput) {
eleOutput = document.createElement('canvas');
}
var contextBlur = eleOutput.getContext('2d');
// 资源的宽高尺寸
var width = 0;
var height = 0;
// 模糊绘制的主方法
var funDrawBlur = function () {
// 绘制画布的尺寸
eleOutput.width = width;
eleOutput.height = height;
// 填充底部图像
contextBlur.clearRect(0, 0, width, height);
contextBlur.drawImage(eleSource, 0, 0);
// 如果没有模糊半径,直接返回
if (!numRadius || typeof numRadius != 'number') {
if (typeof funOutput == 'function') {
funOutput(eleOutput);
}
return;
}
// 创建一个屏外canvas
var canvasOff = document.createElement('canvas');
canvasOff.width = eleOutput.width;
canvasOff.height = eleOutput.height;
var contextOff = canvasOff.getContext('2d');
// 模糊范围处理
if (!arrBounding || !arrBounding.map || arrBounding.length != 4) {
arrBounding = [0, 0, width, height];
}
// 超出范围的处理
if (arrBounding[0] < 0) {
arrBounding[0] = 0;
}
if (arrBounding[1] < 0) {
arrBounding[1] = 0;
}
if (arrBounding[0] + arrBounding[2] > width) {
arrBounding[2] = width - arrBounding[0];
}
if (arrBounding[1] + arrBounding[3] > height) {
arrBounding[3] = height - arrBounding[1];
}
// 绘制局部图像的参数们
var arrParamsDraw = [eleSource].concat(arrBounding, arrBounding);
// 执行模糊
if (typeof contextBlur.filter == 'string') {
// 支持canvas滤镜
contextOff.filter = 'blur(' + numRadius + 'px)';
// 绘制局部图像
contextOff.drawImage.apply(contextOff, arrParamsDraw);
// 上层图像绘制
contextBlur.drawImage(canvasOff, 0, 0);
} else {
// 不支持Canvas滤镜
// 使用第三方方法
var blurFixed = new Blur({
radius: numRadius,
// 高斯算法性能较差
gaussian : false
});
blurFixed.init();
// 先绘制局部图像
contextOff.drawImage.apply(contextOff, arrParamsDraw);
// 再执行模糊
var imgBlured = blurFixed.blurRGBA(canvasOff, null, true);
// 上层图像绘制
contextBlur.drawImage(imgBlured, 0, 0);
}
if (typeof funOutput == 'function') {
funOutput(eleOutput);
}
};
// 如果是 canvas 元素
if (eleSource.getContext) {
width = eleSource.width || 300;
height = eleSource.height || 100;
funDrawBlur();
} else if (eleSource.naturalWidth) {
width = eleSource.naturalWidth;
height = eleSource.naturalHeight;
funDrawBlur();
} else {
eleSource.onload = function () {
width = this.naturalWidth;
height = this.naturalHeight;
funDrawBlur();
// 去除 onload
eleSource.onload = null;
};
}
};
/**
* From: https://github.com/finscn/SimpleBlur
* For: IE、Safari
*/
(function(exports) {
var Blur = exports.Blur = function(options) {
for (var key in options) {
this[key] = options[key];
}
this.init();
};
var proto = {
constructor: Blur,
radius: 1,
gaussian: false,
matrix: null,
init: function() {
this.setRadius(this.radius);
},
setRadius: function(radius) {
this.radius = Math.ceil(radius || 1);
if (this.gaussian) {
this.matrix = this.makeMatrix(this.radius);
}
},
useGaussian: function() {
this.gaussian = true;
this.setRadius(this.radius);
},
useStack: function() {
this.gaussian = false;
this.matrix = null;
},
makeMatrix: function(radius) {
var r = Math.ceil(radius);
var rows = r * 2 + 1;
var matrix = [];
var sigma = radius / 3;
var sigma22 = 2 * sigma * sigma;
var sigmaPi2 = 2 * Math.PI * sigma;
var sqrtSigmaPi2 = Math.sqrt(sigmaPi2);
var radius2 = radius * radius;
var total = 0;
var index = 0;
for (var row = -r; row <= r; row++) {
var distance = row * row;
if (distance > radius2)
matrix[index] = 0;
else
matrix[index] = Math.exp(-(distance) / sigma22) / sqrtSigmaPi2;
total += matrix[index];
index++;
}
for (var i = 0; i < rows; i++) {
matrix[i] /= total;
}
return matrix;
},
doGaussian: function(pixels, width, height, outPixels) {
outPixels = outPixels || pixels;
var radius = this.radius;
var matrix = this.matrix;
var buff = [];
var idx = 0;
var k;
for (var y = 0; y < height; y++) {
var offset = y * width;
for (var x = 0; x < width; x++, idx += 4) {
var r = 0,
g = 0,
b = 0,
a = 0;
for (var n = 0, i = -radius; i <= radius; ++i, ++n) {
var ix = x + i;
if (ix < 0) {
ix = 0;
} else if (ix >= width) {
ix = width - 1;
}
var f = matrix[n];
var idxF = (offset + ix) << 2;
var alpha = pixels[idxF + 3];
k = alpha / 255;
r += pixels[idxF] * f * k;
g += pixels[idxF + 1] * f * k;
b += pixels[idxF + 2] * f * k;
a += alpha * f;
}
if (a == 0) {
k = 0
} else {
k = 255 / a;
}
buff[idx] = r * k;
buff[idx + 1] = g * k;
buff[idx + 2] = b * k;
buff[idx + 3] = a;
}
}
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
var idx = (y * width + x) << 2;
var r = 0,
g = 0,
b = 0,
a = 0;
for (var n = 0, i = -radius; i <= radius; ++i, ++n) {
var iy = y + i;
if (iy < 0) {
iy = 0;
} else if (iy >= height) {
iy = height - 1;
}
var f = matrix[n];
var idxF = (iy * width + x) << 2;
var alpha = buff[idxF + 3];
k = alpha / 255;
r += buff[idxF] * f * k;
g += buff[idxF + 1] * f * k;
b += buff[idxF + 2] * f * k;
a += alpha * f;
}
if (a == 0) {
outPixels[idx] = outPixels[idx + 1] = outPixels[idx + 2] = outPixels[idx + 3] = 0;
} else {
k = 255 / a;
outPixels[idx] = this.clamp(r * k);
outPixels[idx + 1] = this.clamp(g * k);
outPixels[idx + 2] = this.clamp(b * k);
outPixels[idx + 3] = this.clamp(a);
}
}
}
},
doStack: function(pixels, width, height) {
var radius = this.radius;
var i, p, np, r_sum, g_sum, b_sum, a_sum,
r_out_sum, g_out_sum, b_out_sum, a_out_sum,
r_in_sum, g_in_sum, b_in_sum, a_in_sum,
pr, pg, pb, pa, rbs;
var mul_sum = Blur.mul_table[radius],
shg_sum = Blur.shg_table[radius];
var widthMinus1 = width - 1,
heightMinus1 = height - 1,
radiusPlus1 = radius + 1,
sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2,
div = radius + radius + 1,
w4 = width << 2;
var stackStart = Blur.newBlurStack(),
stackEnd = null,
stackIn = null,
stackOut = null,
stack = stackStart;
for (i = 1; i < div; i++) {
stack = stack.next = Blur.newBlurStack();
if (i == radiusPlus1) {
stackEnd = stack;
}
}
stack.next = stackStart;
var k;
var pix1, count1, pix2, count2;
var isH = true;
count1 = height;
count2 = width;
while (true) {
var jump = 0;
var pxIdx = 0;
for (pix1 = 0; pix1 < count1;) {
r_sum = g_sum = b_sum = a_sum = 0;
r_in_sum = g_in_sum = b_in_sum = a_in_sum = 0;
pa = pixels[pxIdx + 3];
k = pa / 255;
pr = pixels[pxIdx] * k;
pg = pixels[pxIdx + 1] * k;
pb = pixels[pxIdx + 2] * k;
r_out_sum = radiusPlus1 * pr
g_out_sum = radiusPlus1 * pg
b_out_sum = radiusPlus1 * pb
a_out_sum = radiusPlus1 * pa
r_sum += sumFactor * pr;
g_sum += sumFactor * pg;
b_sum += sumFactor * pb;
a_sum += sumFactor * pa;
stack = stackStart;
for (i = 0; i < radiusPlus1; i++) {
stack.r = pr;
stack.g = pg;
stack.b = pb;
stack.a = pa;
stack = stack.next;
}
for (i = 1; i < radiusPlus1; i++) {
if (isH) {
p = pxIdx + ((widthMinus1 < i ? widthMinus1 : i) << 2);
} else {
if (i < heightMinus1) {
jump += width;
}
p = pxIdx + jump << 2;
}
pa = pixels[p + 3];
k = pa / 255;
pr = pixels[p] * k;
pg = pixels[p + 1] * k;
pb = pixels[p + 2] * k;
rbs = radiusPlus1 - i;
r_sum += (stack.r = pr) * rbs;
g_sum += (stack.g = pg) * rbs;
b_sum += (stack.b = pb) * rbs;
a_sum += (stack.a = pa) * rbs;
r_in_sum += pr;
g_in_sum += pg;
b_in_sum += pb;
a_in_sum += pa;
stack = stack.next;
}
stackIn = stackStart;
stackOut = stackEnd;
for (pix2 = 0; pix2 < count2; pix2++) {
if (isH) {
np = (jump + ((np = pix2 + radiusPlus1) < widthMinus1 ? np : widthMinus1)) << 2;
} else {
np = (pix1 + (((np = pix2 + radiusPlus1) < heightMinus1 ? np : heightMinus1) * width)) << 2;
}
var _a = ((a_sum * mul_sum) >> shg_sum);
if (_a === 0) {
_r = _g = _b = 0;
} else {
k = 255 / _a;
var _r = ((r_sum * mul_sum) >> shg_sum) * k;
var _g = ((g_sum * mul_sum) >> shg_sum) * k;
var _b = ((b_sum * mul_sum) >> shg_sum) * k;
}
pixels[pxIdx] = this.clamp(_r);
pixels[pxIdx + 1] = this.clamp(_g);
pixels[pxIdx + 2] = this.clamp(_b);
pixels[pxIdx + 3] = this.clamp(_a);
r_sum -= r_out_sum;
g_sum -= g_out_sum;
b_sum -= b_out_sum;
a_sum -= a_out_sum;
r_out_sum -= stackIn.r;
g_out_sum -= stackIn.g;
b_out_sum -= stackIn.b;
a_out_sum -= stackIn.a;
var npa = pixels[np + 3];
k = npa / 255;
var npr = pixels[np] * k;
var npg = pixels[np + 1] * k;
var npb = pixels[np + 2] * k;
r_sum += (r_in_sum += (stackIn.r = npr));
g_sum += (g_in_sum += (stackIn.g = npg));
b_sum += (b_in_sum += (stackIn.b = npb));
a_sum += (a_in_sum += (stackIn.a = npa));
r_out_sum += (pr = stackOut.r);
g_out_sum += (pg = stackOut.g);
b_out_sum += (pb = stackOut.b);
a_out_sum += (pa = stackOut.a);
r_in_sum -= pr;
g_in_sum -= pg;
b_in_sum -= pb;
a_in_sum -= pa;
stackIn = stackIn.next;
stackOut = stackOut.next;
if (isH) {
pxIdx += 4;
} else {
pxIdx += w4;
}
}
pix1++;
if (isH) {
jump += width;
} else {
jump = 0;
pxIdx = pix1 << 2;
}
}
if (!isH) {
break;
}
isH = false;
count1 = width;
count2 = height;
}
},
blurRGBA: function(canvas, outCanvas, ext) {
var radius = this.radius;
if (outCanvas === true || outCanvas === false) {
ext = outCanvas;
outCanvas = null;
}
var sx = 0,
sy = 0,
width = canvas.width,
height = canvas.height;
if (!canvas.getContext || ext) {
var img = canvas;
canvas = this.createSourceCanvas(img, sx, sy, width, height, radius, ext);
if (ext !== false) {
width += radius * 2,
height += radius * 2;
}
if (!outCanvas) {
outCanvas = canvas;
}
}
if (!outCanvas) {
width += radius * 2,
height += radius * 2;
outCanvas = this.createCanvas(width, height);
}
var context = canvas.getContext('2d');
var outContext = outCanvas.getContext('2d');
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
var pixels = imageData.data;
if (this.gaussian) {
this.doGaussian(pixels, width, height);
} else {
this.doStack(pixels, width, height);
}
outContext.putImageData(imageData, sx, sy);
return outCanvas;
},
createSourceCanvas: function(img, sx, sy, sw, sh, radius, ext) {
var dx = 0,
dy = 0;
var width = sw,
height = sh;
if (ext !== false) {
dx = radius;
dy = radius;
width += radius * 2,
height += radius * 2;
}
var canvas = this.createCanvas(width, height);
var context = canvas.getContext('2d');
context.drawImage(img, sx, sy, sw, sh, dx, dy, sw, sh);
return canvas;
},
clamp: function(v) {
return v < 255 ? (v > 0 ? (v | 0) : 0) : 255;
},
createCanvas: function(width, height) {
var canvas = document.createElement("canvas");
canvas.retinaResolutionEnabled = false;
canvas.width = width;
canvas.height = height;
// canvas.MSAAEnabled = true;
// canvas.MSAASamples = 4;
return canvas;
},
};
Blur.newBlurStack = function() {
return {
r: 0,
g: 0,
b: 0,
a: 0,
next: null
}
};
Blur.mul_table = [
512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
];
Blur.shg_table = [
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
];
for (var p in proto) {
Blur.prototype[p] = proto[p];
}
}(window));
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
JavaScript
1
https://gitee.com/zhizous/local-blur.git
git@gitee.com:zhizous/local-blur.git
zhizous
local-blur
图像局部高斯模糊
master

搜索帮助