基于JavaScript實現(xiàn)線性漸變的高斯模糊效果
本文首先介紹了高斯模糊算法然后敘述了線性漸變的高斯模糊算法的原理,最后通過一個小demo展示了如何實現(xiàn)y方向上線性漸變的高斯模糊效果。
1. 高斯模糊算法
高斯模糊是圖像處理中一種常用的模糊技術(shù),它通過數(shù)學(xué)上的高斯函數(shù)來實現(xiàn)對圖像的平滑處理。這種模糊效果看起來像是圖像被輕微地“散開”,能夠有效地減少圖像噪聲和細(xì)節(jié),從而產(chǎn)生一種柔和的視覺效果。高斯模糊原理詳細(xì)說明如下:
高斯函數(shù)
高斯函數(shù)(或高斯分布)是一種鐘形的、平滑的曲線,其數(shù)學(xué)表達(dá)式為:
其中,σ是標(biāo)準(zhǔn)差,決定了曲線的寬度。在圖像處理中,高斯函數(shù)用于創(chuàng)建一個稱為“高斯核”的權(quán)重矩陣。
高斯核
高斯核是一個二維數(shù)組,用于根據(jù)高斯函數(shù)的值來給圖像中的每個像素及其鄰域像素分配權(quán)重。核的中心值最大,表示對當(dāng)前像素點的權(quán)重最高,而遠(yuǎn)離中心的像素權(quán)重逐漸減小。
模糊過程
在進行高斯模糊時,算法會遍歷圖像的每個像素點,并對每個像素及其周圍的像素應(yīng)用高斯核。具體操作如下:
- 遍歷像素點: 對圖像中的每個像素點進行遍歷。
- 應(yīng)用高斯核: 對于每個像素點,將其鄰域內(nèi)的像素值與高斯核中對應(yīng)位置的權(quán)重相乘,并對結(jié)果求和。
- 更新像素值: 將上述步驟得到的加權(quán)和作為新的像素值。這樣,每個像素點的新值是其本身及鄰域像素值的加權(quán)平均。
模糊效果
應(yīng)用高斯模糊后,由于鄰域像素的加權(quán)平均作用,圖像中的每個像素都會與周圍像素融合,從而產(chǎn)生平滑和模糊的效果。這種模糊處理可以減少圖像的細(xì)節(jié)和噪聲,使圖像看起來更加柔和。
應(yīng)用場景
高斯模糊在許多領(lǐng)域都有應(yīng)用,包括但不限于:
- 圖像美化: 使圖像看起來更柔和、自然。
- 去除噪聲: 減少圖像的隨機噪聲。
- 圖像分割: 在圖像處理的預(yù)處理階段,為了更好地進行邊緣檢測或者特征提取。
高斯模糊的關(guān)鍵在于合理選擇高斯核的大小(σ\sigmaσ值),這直接影響到模糊的程度和效果。
2. 有梯度的高斯模糊效果的實現(xiàn)
要在y方向上實現(xiàn)有梯度的高斯模糊,可以通過動態(tài)調(diào)整高斯核的標(biāo)準(zhǔn)差(σ值)來實現(xiàn)。具體來說,隨著y坐標(biāo)的變化,逐漸增加或減少σ值,從而在垂直方向上產(chǎn)生漸變的模糊效果。
1. 動態(tài)調(diào)整σ值
對于圖像中的每一行,根據(jù)其y坐標(biāo)動態(tài)計算σ值??梢栽O(shè)計一個函數(shù),使σ值從圖像頂部到底部線性增加或減少。
2. 生成高斯核
針對每一行的σ值,生成對應(yīng)的高斯核。由于每行的σ值不同,所以每行的高斯核也將不同。
3. 應(yīng)用高斯模糊
使用生成的高斯核對每一行進行模糊處理。具體來說,對于圖像中的每個像素,使用其所在行的高斯核進行加權(quán)平均計算,得到新的像素值。
4. 實現(xiàn)梯度效果
由于每行的模糊程度隨σ值變化,因此整個圖像在垂直方向上將呈現(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值生成高斯核... }
這個函數(shù)會根據(jù)y坐標(biāo)的變化來調(diào)整σ值,并為每一行生成相應(yīng)的高斯核,從而實現(xiàn)在y方向上的漸變高斯模糊效果。
3. 構(gòu)建測試框架
為了對比采用線性漸變的高斯模糊效果,需要搭建一個簡單的html文件用來對比應(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ì)解釋
這段代碼實現(xiàn)了一個帶有漸變高斯模糊效果的圖像處理示例。下面分別對代碼中的關(guān)鍵部分進行解釋:
img.onload 回調(diào)函數(shù)
當(dāng)圖像加載完成后,這個回調(diào)函數(shù)被觸發(fā)。它首先設(shè)置畫布的尺寸以匹配圖像的尺寸。接著,它通過一個循環(huán),分塊地將圖像繪制到畫布上,同時應(yīng)用不同程度的模糊效果。這種逐漸增加模糊度的效果通過調(diào)整 ctx.filter
屬性來實現(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ù)
這個函數(shù)實現(xiàn)了高斯模糊算法。它接收一個繪圖上下文(ctx
),圖像的寬度和高度,以及一個控制模糊范圍的位置參數(shù)(pos
)。函數(shù)首先獲取畫布上當(dāng)前的圖像數(shù)據(jù)。然后,它對每個像素應(yīng)用高斯模糊,基于高斯核和每個像素周圍的像素值來計算新的像素值。
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++) { // 對于每一行,根據(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ù)
這個函數(shù)根據(jù)行號和圖像高度計算高斯模糊的 sigma
值。這里使用了一個簡單的線性關(guān)系來確定 sigma
的大小,從而創(chuàng)建一種模糊程度隨位置變化的效果。
function calculateSigmaForRow(row, height) { // 示例:線性增長的sigma // 你可以根據(jù)需要調(diào)整這個函數(shù)來改變sigma的計算方式 const maxSigma = 15; // 最大sigma值 return maxSigma * (row / height); }
getGaussianKernel 函數(shù)
此函數(shù)生成一個高斯核,這是實現(xiàn)高斯模糊的關(guān)鍵。高斯核是一個二維數(shù)組,表示每個像素與其周圍像素的權(quán)重。這個核在 applyGaussianBlur
函數(shù)中用于計算每個像素的新值。
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)漸變的高斯模糊效果。這通過分段繪制圖像并逐步增加模糊程度來實現(xiàn)。img.onload
回調(diào)函數(shù)處理圖像的加載和初步處理,而 applyGaussianBlur
函數(shù)則負(fù)責(zé)應(yīng)用高斯模糊算法。calculateSigmaForRow
和 getGaussianKernel
函數(shù)提供了實現(xiàn)高斯模糊所需的數(shù)學(xué)計算。
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++) { // 對于每一行,根據(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) { // 示例:線性增長的sigma // 你可以根據(jù)需要調(diào)整這個函數(shù)來改變sigma的計算方式 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實現(xiàn)線性漸變的高斯模糊效果的詳細(xì)內(nèi)容,更多關(guān)于JavaScript高斯模糊的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js簡單實現(xiàn)Select互換數(shù)據(jù)的方法
這篇文章主要介紹了js簡單實現(xiàn)Select互換數(shù)據(jù)的方法,涉及javascript動態(tài)操作select中option節(jié)點的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-08-08js判斷數(shù)組key是否存在(不用循環(huán))的簡單實例
下面小編就為大家?guī)硪黄猨s判斷數(shù)組key是否存在(不用循環(huán))的簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-08-08JavaScript中將字符串轉(zhuǎn)換為浮點數(shù)的技巧
在JavaScript中,parseFloat是一個內(nèi)置函數(shù),用于將一個字符串解析成浮點數(shù),這個方法對于處理用戶輸入、從文本文件讀取數(shù)據(jù)或者在Web API中獲取數(shù)值尤其重要,本文將詳細(xì)介紹parseFloat的工作原理、使用方法以及通過代碼案例展示其在實際開發(fā)中的應(yīng)用2025-02-02three.js顯示中文字體與tween應(yīng)用詳析
這篇文章主要給大家介紹了關(guān)于three.js顯示中文字體與tween應(yīng)用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01使用 JavaScript如何獲取當(dāng)月的第一天和最后一天
這篇文章主要介紹了使用 JavaScript如何獲取當(dāng)月的第一天和最后一天,通過本文學(xué)習(xí)了如何使用 JavaScript 中的Date.getFullYear()和?Date.getMonth()方法獲得某個特定月份的第一天和最后一天,需要的朋友可以參考下2023-05-05小心!AngularJS結(jié)合RequireJS做文件合并壓縮的那些坑
小心!AngularJS結(jié)合RequireJS做文件合并壓縮的那些坑,大家在做文件合并壓縮的時候一定要注意,感興趣的朋友可以參考一下2016-01-01詳解JavaScript時間處理之幾個月前或幾個月后的指定日期
本篇文章主要介紹了JavaScript時間處理之幾個月前或幾個月后的指定日期 ,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-12-12