基于JavaScript实现线性渐变的高斯模糊效果

 更新时间:2024年01月31日 08:23:03   作者:慕仲卿  
这篇文章主要为大家详细介绍了高斯模糊算法以及线性渐变的高斯模糊算法的原理,并通过一个小demo展示了如何实现y方向上线性渐变的高斯模糊效果,需要的可以了解下

本文首先介绍了高斯模糊算法然后叙述了线性渐变的高斯模糊算法的原理,最后通过一个小demo展示了如何实现y方向上线性渐变的高斯模糊效果。

1. 高斯模糊算法

高斯模糊是图像处理中一种常用的模糊技术,它通过数学上的高斯函数来实现对图像的平滑处理。这种模糊效果看起来像是图像被轻微地“散开”,能够有效地减少图像噪声和细节,从而产生一种柔和的视觉效果。高斯模糊原理详细说明如下:

高斯函数

高斯函数(或高斯分布)是一种钟形的、平滑的曲线,其数学表达式为:

其中,σ是标准差,决定了曲线的宽度。在图像处理中,高斯函数用于创建一个称为“高斯核”的权重矩阵。

高斯核

高斯核是一个二维数组,用于根据高斯函数的值来给图像中的每个像素及其邻域像素分配权重。核的中心值最大,表示对当前像素点的权重最高,而远离中心的像素权重逐渐减小。

模糊过程

在进行高斯模糊时,算法会遍历图像的每个像素点,并对每个像素及其周围的像素应用高斯核。具体操作如下:

  • 遍历像素点: 对图像中的每个像素点进行遍历。
  • 应用高斯核: 对于每个像素点,将其邻域内的像素值与高斯核中对应位置的权重相乘,并对结果求和。
  • 更新像素值: 将上述步骤得到的加权和作为新的像素值。这样,每个像素点的新值是其本身及邻域像素值的加权平均。

模糊效果

应用高斯模糊后,由于邻域像素的加权平均作用,图像中的每个像素都会与周围像素融合,从而产生平滑和模糊的效果。这种模糊处理可以减少图像的细节和噪声,使图像看起来更加柔和。

应用场景

高斯模糊在许多领域都有应用,包括但不限于:

  • 图像美化: 使图像看起来更柔和、自然。
  • 去除噪声: 减少图像的随机噪声。
  • 图像分割: 在图像处理的预处理阶段,为了更好地进行边缘检测或者特征提取。

高斯模糊的关键在于合理选择高斯核的大小(σ\sigmaσ值),这直接影响到模糊的程度和效果。

2. 有梯度的高斯模糊效果的实现

要在y方向上实现有梯度的高斯模糊,可以通过动态调整高斯核的标准差(σ值)来实现。具体来说,随着y坐标的变化,逐渐增加或减少σ值,从而在垂直方向上产生渐变的模糊效果。

1. 动态调整σ值

对于图像中的每一行,根据其y坐标动态计算σ值。可以设计一个函数,使σ值从图像顶部到底部线性增加或减少。

2. 生成高斯核

针对每一行的σ值,生成对应的高斯核。由于每行的σ值不同,所以每行的高斯核也将不同。

3. 应用高斯模糊

使用生成的高斯核对每一行进行模糊处理。具体来说,对于图像中的每个像素,使用其所在行的高斯核进行加权平均计算,得到新的像素值。

4. 实现梯度效果

由于每行的模糊程度随σ值变化,因此整个图像在垂直方向上将呈现出渐变的高斯模糊效果。

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);
        // 应用高斯模糊到这一行...
    }

    ctx.putImageData(copy, 0, 0);
}

function calculateSigmaForRow(row, height) {
    const maxSigma = 5;  // 假设最大sigma值为5
    return maxSigma * (row / height); // 使sigma值随y坐标线性增加
}

function getGaussianKernel(sigma) {
    // 根据sigma值生成高斯核...
}

这个函数会根据y坐标的变化来调整σ值,并为每一行生成相应的高斯核,从而实现在y方向上的渐变高斯模糊效果。

3. 构建测试框架

为了对比采用线性渐变的高斯模糊效果,需要搭建一个简单的html文件用来对比应用前后的效果:

<!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. 详细解释

这段代码实现了一个带有渐变高斯模糊效果的图像处理示例。下面分别对代码中的关键部分进行解释:

img.onload 回调函数

当图像加载完成后,这个回调函数被触发。它首先设置画布的尺寸以匹配图像的尺寸。接着,它通过一个循环,分块地将图像绘制到画布上,同时应用不同程度的模糊效果。这种逐渐增加模糊度的效果通过调整 ctx.filter 属性来实现。

            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 函数

这个函数实现了高斯模糊算法。它接收一个绘图上下文(ctx),图像的宽度和高度,以及一个控制模糊范围的位置参数(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++) {
                // 对于每一行,根据行号计算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 函数

这个函数根据行号和图像高度计算高斯模糊的 sigma 值。这里使用了一个简单的线性关系来确定 sigma 的大小,从而创建一种模糊程度随位置变化的效果。

        function calculateSigmaForRow(row, height) {
            // 示例:线性增长的sigma
            // 你可以根据需要调整这个函数来改变sigma的计算方式
            const maxSigma = 15; // 最大sigma值
            return maxSigma * (row / height);
        }

getGaussianKernel 函数

此函数生成一个高斯核,这是实现高斯模糊的关键。高斯核是一个二维数组,表示每个像素与其周围像素的权重。这个核在 applyGaussianBlur 函数中用于计算每个像素的新值。

        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;
        }

整体思路

代码的主要思想是在图像的一部分区域实现渐变的高斯模糊效果。这通过分段绘制图像并逐步增加模糊程度来实现。img.onload 回调函数处理图像的加载和初步处理,而 applyGaussianBlur 函数则负责应用高斯模糊算法。calculateSigmaForRowgetGaussianKernel 函数提供了实现高斯模糊所需的数学计算。

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++) {
                // 对于每一行,根据行号计算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
            // 你可以根据需要调整这个函数来改变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实现线性渐变的高斯模糊效果的详细内容,更多关于JavaScript高斯模糊的资料请关注脚本之家其它相关文章!

相关文章

  • 深入理解JS addLoadEvent函数

    深入理解JS addLoadEvent函数

    下面小编就为大家带来一篇深入理解JS addLoadEvent函数。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-05-05
  • 详解js的事件处理函数和动态创建html标记方法

    详解js的事件处理函数和动态创建html标记方法

    本文主要对javascript的事件处理函数,动态创建html标记的两种方法进行详细介绍,具有很好的参考价值,需要的朋友一起来看下吧
    2016-12-12
  • 关于promise.all()的使用及说明

    关于promise.all()的使用及说明

    这篇文章主要介绍了关于promise.all()的使用及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • 详解JS获取HTML DOM元素的8种方法

    详解JS获取HTML DOM元素的8种方法

    本篇文章主要介绍了详解JS获取HTML DOM元素的8种方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • JavaScript数据结构与算法之队列原理与用法实例详解

    JavaScript数据结构与算法之队列原理与用法实例详解

    这篇文章主要介绍了JavaScript数据结构与算法之队列原理与用法,较为详细的说明了队列的概念、原理,并结合实例形式分析了javascript实现与使用队列的相关操作技巧与注意事项,需要的朋友可以参考下
    2017-11-11
  • 微信小程序实现时间进度条功能

    微信小程序实现时间进度条功能

    这篇文章主要为大家详细介绍了微信小程序实现时间进度条功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-11-11
  • javascript 也来玩玩图片预加载

    javascript 也来玩玩图片预加载

    javascript 也来玩玩图片预加载...
    2007-05-05
  • 纯js模拟div层弹性运动的方法

    纯js模拟div层弹性运动的方法

    这篇文章主要介绍了纯js模拟div层弹性运动的方法,涉及javascript操作div层实现运动的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-07-07
  • 解决JS浮点数运算出现Bug的方法

    解决JS浮点数运算出现Bug的方法

    解决JS浮点数运算出现Bug的方法,需要的朋友可以参考一下
    2013-03-03
  • 微信小程序实现简单计算器功能

    微信小程序实现简单计算器功能

    这篇文章主要为大家详细介绍了微信小程序实现简单计算器功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07

最新评论