基于JavaScript實(shí)現(xiàn)線性漸變的高斯模糊效果
本文首先介紹了高斯模糊算法然后敘述了線性漸變的高斯模糊算法的原理,最后通過(guò)一個(gè)小demo展示了如何實(shí)現(xiàn)y方向上線性漸變的高斯模糊效果。
1. 高斯模糊算法
高斯模糊是圖像處理中一種常用的模糊技術(shù),它通過(guò)數(shù)學(xué)上的高斯函數(shù)來(lái)實(shí)現(xiàn)對(duì)圖像的平滑處理。這種模糊效果看起來(lái)像是圖像被輕微地“散開”,能夠有效地減少圖像噪聲和細(xì)節(jié),從而產(chǎn)生一種柔和的視覺效果。高斯模糊原理詳細(xì)說(shuō)明如下:
高斯函數(shù)
高斯函數(shù)(或高斯分布)是一種鐘形的、平滑的曲線,其數(shù)學(xué)表達(dá)式為:

其中,σ是標(biāo)準(zhǔn)差,決定了曲線的寬度。在圖像處理中,高斯函數(shù)用于創(chuàng)建一個(gè)稱為“高斯核”的權(quán)重矩陣。
高斯核
高斯核是一個(gè)二維數(shù)組,用于根據(jù)高斯函數(shù)的值來(lái)給圖像中的每個(gè)像素及其鄰域像素分配權(quán)重。核的中心值最大,表示對(duì)當(dāng)前像素點(diǎn)的權(quán)重最高,而遠(yuǎn)離中心的像素權(quán)重逐漸減小。
模糊過(guò)程
在進(jìn)行高斯模糊時(shí),算法會(huì)遍歷圖像的每個(gè)像素點(diǎn),并對(duì)每個(gè)像素及其周圍的像素應(yīng)用高斯核。具體操作如下:
- 遍歷像素點(diǎn): 對(duì)圖像中的每個(gè)像素點(diǎn)進(jìn)行遍歷。
- 應(yīng)用高斯核: 對(duì)于每個(gè)像素點(diǎn),將其鄰域內(nèi)的像素值與高斯核中對(duì)應(yīng)位置的權(quán)重相乘,并對(duì)結(jié)果求和。
- 更新像素值: 將上述步驟得到的加權(quán)和作為新的像素值。這樣,每個(gè)像素點(diǎn)的新值是其本身及鄰域像素值的加權(quán)平均。
模糊效果
應(yīng)用高斯模糊后,由于鄰域像素的加權(quán)平均作用,圖像中的每個(gè)像素都會(huì)與周圍像素融合,從而產(chǎn)生平滑和模糊的效果。這種模糊處理可以減少圖像的細(xì)節(jié)和噪聲,使圖像看起來(lái)更加柔和。
應(yīng)用場(chǎng)景
高斯模糊在許多領(lǐng)域都有應(yīng)用,包括但不限于:
- 圖像美化: 使圖像看起來(lái)更柔和、自然。
- 去除噪聲: 減少圖像的隨機(jī)噪聲。
- 圖像分割: 在圖像處理的預(yù)處理階段,為了更好地進(jìn)行邊緣檢測(cè)或者特征提取。
高斯模糊的關(guān)鍵在于合理選擇高斯核的大?。?sigma;\sigmaσ值),這直接影響到模糊的程度和效果。
2. 有梯度的高斯模糊效果的實(shí)現(xiàn)
要在y方向上實(shí)現(xiàn)有梯度的高斯模糊,可以通過(guò)動(dòng)態(tài)調(diào)整高斯核的標(biāo)準(zhǔn)差(σ值)來(lái)實(shí)現(xiàn)。具體來(lái)說(shuō),隨著y坐標(biāo)的變化,逐漸增加或減少σ值,從而在垂直方向上產(chǎn)生漸變的模糊效果。
1. 動(dòng)態(tài)調(diào)整σ值
對(duì)于圖像中的每一行,根據(jù)其y坐標(biāo)動(dòng)態(tài)計(jì)算σ值。可以設(shè)計(jì)一個(gè)函數(shù),使σ值從圖像頂部到底部線性增加或減少。
2. 生成高斯核
針對(duì)每一行的σ值,生成對(duì)應(yīng)的高斯核。由于每行的σ值不同,所以每行的高斯核也將不同。
3. 應(yīng)用高斯模糊
使用生成的高斯核對(duì)每一行進(jìn)行模糊處理。具體來(lái)說(shuō),對(duì)于圖像中的每個(gè)像素,使用其所在行的高斯核進(jìn)行加權(quán)平均計(jì)算,得到新的像素值。
4. 實(shí)現(xiàn)梯度效果
由于每行的模糊程度隨σ值變化,因此整個(gè)圖像在垂直方向上將呈現(xiàn)出漸變的高斯模糊效果。
5. 偽代碼
function applyGradientGaussianBlur(ctx, width, height) {
const copy = ctx.getImageData(0, 0, width, height);
const copyData = copy.data;
for (let y = 0; y < height; y++) {
const sigma = calculateSigmaForRow(y, height);
const kernel = getGaussianKernel(sigma);
// 應(yīng)用高斯模糊到這一行...
}
ctx.putImageData(copy, 0, 0);
}
function calculateSigmaForRow(row, height) {
const maxSigma = 5; // 假設(shè)最大sigma值為5
return maxSigma * (row / height); // 使sigma值隨y坐標(biāo)線性增加
}
function getGaussianKernel(sigma) {
// 根據(jù)sigma值生成高斯核...
}
這個(gè)函數(shù)會(huì)根據(jù)y坐標(biāo)的變化來(lái)調(diào)整σ值,并為每一行生成相應(yīng)的高斯核,從而實(shí)現(xiàn)在y方向上的漸變高斯模糊效果。
3. 構(gòu)建測(cè)試框架
為了對(duì)比采用線性漸變的高斯模糊效果,需要搭建一個(gè)簡(jiǎn)單的html文件用來(lái)對(duì)比應(yīng)用前后的效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>高斯模糊示例</title>
<style>
body {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<div style="margin-right: 50px;">
<h1>模糊之后</h1>
<canvas id="canvas"></canvas>
</div>
<div>
<h1>模糊之前</h1>
<canvas id="canvas2"></canvas>
</div>
<script>
window.onload = function () {
const canvas = document.getElementById('canvas');
const canvas2 = document.getElementById('canvas2');
const ctx = canvas.getContext('2d');
const ctx2 = canvas2.getContext('2d');
const img = new Image();
img.src = 'demo.jpg'; // 替換為你的圖片路徑
img.onload = function () {
...
};
};
function applyGaussianBlur(ctx, width, height, pos) {
...
}
function calculateSigmaForRow(row, height) {
...
}
function getGaussianKernel(size, sigma) {
...
}
</script>
</body>
</html>
4. 詳細(xì)解釋
這段代碼實(shí)現(xiàn)了一個(gè)帶有漸變高斯模糊效果的圖像處理示例。下面分別對(duì)代碼中的關(guān)鍵部分進(jìn)行解釋:
img.onload 回調(diào)函數(shù)
當(dāng)圖像加載完成后,這個(gè)回調(diào)函數(shù)被觸發(fā)。它首先設(shè)置畫布的尺寸以匹配圖像的尺寸。接著,它通過(guò)一個(gè)循環(huán),分塊地將圖像繪制到畫布上,同時(shí)應(yīng)用不同程度的模糊效果。這種逐漸增加模糊度的效果通過(guò)調(diào)整 ctx.filter 屬性來(lái)實(shí)現(xiàn)。
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
canvas2.width = img.width;
canvas2.height = img.height;
// ctx.drawImage(img, 0, 0);
// 分10份逐份繪制
const numOfParts = 200;
const pos = 3 / 2;
const parts = 20;
const partHeight = img.height / numOfParts;
for (let i = 0; i < numOfParts; i++) {
if(i>numOfParts/pos){
ctx.filter = `blur(${1.5*(i/parts - numOfParts/pos/parts)}px)`;
console.log('i/parts: ', i/parts - numOfParts/pos/parts)
} else {
ctx.filter = `blur(${0}px)`;
}
ctx.drawImage(img, 0, i * partHeight, img.width, partHeight, 0, i * partHeight, img.width, partHeight);
}
ctx2.drawImage(img, 0, 0);
applyGaussianBlur(ctx, img.width, img.height, pos);
};
applyGaussianBlur 函數(shù)
這個(gè)函數(shù)實(shí)現(xiàn)了高斯模糊算法。它接收一個(gè)繪圖上下文(ctx),圖像的寬度和高度,以及一個(gè)控制模糊范圍的位置參數(shù)(pos)。函數(shù)首先獲取畫布上當(dāng)前的圖像數(shù)據(jù)。然后,它對(duì)每個(gè)像素應(yīng)用高斯模糊,基于高斯核和每個(gè)像素周圍的像素值來(lái)計(jì)算新的像素值。
function applyGaussianBlur(ctx, width, height, pos) {
const copy = ctx.getImageData(0, 0, width, height);
const copyData = copy.data;
const kernelSize = 16; // 高斯核的固定大小
for (let y = 0; y > height/pos; y++) {
// 對(duì)于每一行,根據(jù)行號(hào)計(jì)算sigma值
const sigma = 5 * calculateSigmaForRow(y, height);
const kernel = getGaussianKernel(kernelSize, sigma);
const half = Math.floor(kernelSize / 2);
for (let x = 0; x < width; x++) {
let r = 0, g = 0, b = 0, total = 0;
for (let ky = -half; ky <= half; ky++) {
for (let kx = -half; kx <= half; kx++) {
const px = x + kx;
const py = y + ky;
if (px >= 0 && px < width && py >= 0 && py < height) {
const p = (py * width + px) * 4;
const weight = kernel[ky + half][kx + half];
r += copyData[p] * weight;
g += copyData[p + 1] * weight;
b += copyData[p + 2] * weight;
total += weight;
}
}
}
const pos = (y * width + x) * 4;
copyData[pos] = r / total;
copyData[pos + 1] = g / total;
copyData[pos + 2] = b / total;
}
}
ctx.putImageData(copy, 0, 0);
}
calculateSigmaForRow 函數(shù)
這個(gè)函數(shù)根據(jù)行號(hào)和圖像高度計(jì)算高斯模糊的 sigma 值。這里使用了一個(gè)簡(jiǎn)單的線性關(guān)系來(lái)確定 sigma 的大小,從而創(chuàng)建一種模糊程度隨位置變化的效果。
function calculateSigmaForRow(row, height) {
// 示例:線性增長(zhǎng)的sigma
// 你可以根據(jù)需要調(diào)整這個(gè)函數(shù)來(lái)改變sigma的計(jì)算方式
const maxSigma = 15; // 最大sigma值
return maxSigma * (row / height);
}
getGaussianKernel 函數(shù)
此函數(shù)生成一個(gè)高斯核,這是實(shí)現(xiàn)高斯模糊的關(guān)鍵。高斯核是一個(gè)二維數(shù)組,表示每個(gè)像素與其周圍像素的權(quán)重。這個(gè)核在 applyGaussianBlur 函數(shù)中用于計(jì)算每個(gè)像素的新值。
function getGaussianKernel(size, sigma) {
const kernel = [];
let sum = 0;
const half = Math.floor(size / 2);
for (let y = -half; y <= half; y++) {
kernel[y + half] = [];
for (let x = -half; x <= half; x++) {
const value = Math.exp(-(x * x + y * y) / (2 * sigma * sigma)) / (2 * Math.PI * sigma * sigma);
kernel[y + half][x + half] = value;
sum += value;
}
}
// 歸一化核
for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
kernel[y][x] /= sum;
}
}
return kernel;
}
整體思路
代碼的主要思想是在圖像的一部分區(qū)域?qū)崿F(xiàn)漸變的高斯模糊效果。這通過(guò)分段繪制圖像并逐步增加模糊程度來(lái)實(shí)現(xiàn)。img.onload 回調(diào)函數(shù)處理圖像的加載和初步處理,而 applyGaussianBlur 函數(shù)則負(fù)責(zé)應(yīng)用高斯模糊算法。calculateSigmaForRow 和 getGaussianKernel 函數(shù)提供了實(shí)現(xiàn)高斯模糊所需的數(shù)學(xué)計(jì)算。
5. 完整代碼及效果展示
效果展示

完整代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>高斯模糊示例</title>
<style>
body {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<div style="margin-right: 50px;">
<h1>模糊之后</h1>
<canvas id="canvas"></canvas>
</div>
<div>
<h1>模糊之前</h1>
<canvas id="canvas2"></canvas>
</div>
<script>
window.onload = function () {
const canvas = document.getElementById('canvas');
const canvas2 = document.getElementById('canvas2');
const ctx = canvas.getContext('2d');
const ctx2 = canvas2.getContext('2d');
const img = new Image();
img.src = 'demo.jpg'; // 替換為你的圖片路徑
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
canvas2.width = img.width;
canvas2.height = img.height;
// ctx.drawImage(img, 0, 0);
// 分10份逐份繪制
const numOfParts = 200;
const pos = 3 / 2;
const parts = 20;
const partHeight = img.height / numOfParts;
for (let i = 0; i < numOfParts; i++) {
if(i>numOfParts/pos){
ctx.filter = `blur(${1.5*(i/parts - numOfParts/pos/parts)}px)`;
console.log('i/parts: ', i/parts - numOfParts/pos/parts)
} else {
ctx.filter = `blur(${0}px)`;
}
ctx.drawImage(img, 0, i * partHeight, img.width, partHeight, 0, i * partHeight, img.width, partHeight);
}
ctx2.drawImage(img, 0, 0);
applyGaussianBlur(ctx, img.width, img.height, pos);
};
};
function applyGaussianBlur(ctx, width, height, pos) {
const copy = ctx.getImageData(0, 0, width, height);
const copyData = copy.data;
const kernelSize = 16; // 高斯核的固定大小
for (let y = 0; y > height/pos; y++) {
// 對(duì)于每一行,根據(jù)行號(hào)計(jì)算sigma值
const sigma = 5 * calculateSigmaForRow(y, height);
const kernel = getGaussianKernel(kernelSize, sigma);
const half = Math.floor(kernelSize / 2);
for (let x = 0; x < width; x++) {
let r = 0, g = 0, b = 0, total = 0;
for (let ky = -half; ky <= half; ky++) {
for (let kx = -half; kx <= half; kx++) {
const px = x + kx;
const py = y + ky;
if (px >= 0 && px < width && py >= 0 && py < height) {
const p = (py * width + px) * 4;
const weight = kernel[ky + half][kx + half];
r += copyData[p] * weight;
g += copyData[p + 1] * weight;
b += copyData[p + 2] * weight;
total += weight;
}
}
}
const pos = (y * width + x) * 4;
copyData[pos] = r / total;
copyData[pos + 1] = g / total;
copyData[pos + 2] = b / total;
}
}
ctx.putImageData(copy, 0, 0);
}
function calculateSigmaForRow(row, height) {
// 示例:線性增長(zhǎng)的sigma
// 你可以根據(jù)需要調(diào)整這個(gè)函數(shù)來(lái)改變sigma的計(jì)算方式
const maxSigma = 15; // 最大sigma值
return maxSigma * (row / height);
}
function getGaussianKernel(size, sigma) {
const kernel = [];
let sum = 0;
const half = Math.floor(size / 2);
for (let y = -half; y <= half; y++) {
kernel[y + half] = [];
for (let x = -half; x <= half; x++) {
const value = Math.exp(-(x * x + y * y) / (2 * sigma * sigma)) / (2 * Math.PI * sigma * sigma);
kernel[y + half][x + half] = value;
sum += value;
}
}
// 歸一化核
for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
kernel[y][x] /= sum;
}
}
return kernel;
}
</script>
</body>
</html>
以上就是基于JavaScript實(shí)現(xiàn)線性漸變的高斯模糊效果的詳細(xì)內(nèi)容,更多關(guān)于JavaScript高斯模糊的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript NaN和Infinity特殊值 [譯]
本文要講的是兩個(gè)特殊值,NaN和Infinity,返回這兩個(gè)值的操作通常都應(yīng)該返回正常的數(shù)字2012-09-09
JavaScript canvas實(shí)現(xiàn)環(huán)形漸變進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了JavaScript canvas實(shí)現(xiàn)環(huán)形漸變進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
js的window.showModalDialog及window.open用法實(shí)例分析
這篇文章主要介紹了js的window.showModalDialog及window.open用法,實(shí)例分析了window.showModalDialog與window.open方法的定義、功能與使用技巧,需要的朋友可以參考下2015-01-01
JavaScript?Canvas實(shí)現(xiàn)圖片局部放大鏡效果
這篇文章主要為大家詳細(xì)介紹了如何使用JavaScript?Canvas實(shí)現(xiàn)圖片局部放大鏡效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03
JS實(shí)現(xiàn)加載和讀取XML文件的方法詳解
這篇文章主要介紹了JS實(shí)現(xiàn)加載和讀取XML文件的方法,結(jié)合實(shí)例形式分析了javascript針對(duì)xml文件的加載與讀取相關(guān)實(shí)現(xiàn)步驟與操作技巧,需要的朋友可以參考下2017-04-04

