Back
PQCHub

平滑濾波與卷積

深入卷積概念,用程式實作均值與高斯濾波,掌握影像平滑技術,創造專業級模糊效果。


影像平滑是影像處理中最常見的操作之一,通常用於:

  • 降低影像噪聲
  • 模糊影像以突出整體結構
  • 預處理以利邊緣檢測或其他特徵提取

1 基本概念

1.1 卷積 (Convolution) 概念

卷積是數位影像處理的核心操作。簡單說,卷積就是:

  1. 選擇一個小矩陣(稱為 卷積核kernel),例如 3×3
  2. 對影像每個像素周圍的區域進行加權求和
  3. 用結果替換原本像素值

數學公式:

\begin{align} I'(x,y) = \sum_{i=-k}^{k} \sum_{j=-k}^{k} I(x+i, y+j) \cdot K(i,j) \end{align}

  • $I$ 是原影像
  • $I'$ 是輸出影像
  • $K$ 是卷積核
  • $k$ 是卷積核的半寬度

提示:卷積本質是對局部像素進行加權平均或其他線性運算。


1.2 卷積的簡單應用:均值濾波 (Mean Filter)

均值濾波是一種最簡單的平滑方法,卷積核每個元素值都相等,通常總和為 1。例如 3×3 均值濾波核:

\begin{align} K = \frac{1}{9} \begin{bmatrix} 1 & 1 & 1\\ 1 & 1 & 1\\ 1 & 1 & 1 \end{bmatrix} \end{align}

效果:

  • 將鄰近像素平均,減少局部雜訊
  • 影像變得平滑,但邊緣可能稍微模糊

1.3 JavaScript 實作 (自寫卷積)

使用之前自製的 BmpImage 類別:

import BmpImage from "./BmpImage.js";

// 建立 3x3 均值卷積核
const meanKernel = [
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9]
];

function convolve(img, kernel) {
    const width = img.width;
    const height = img.height;
    const newImg = BmpImage.create(width, height);

    const kSize = kernel.length;
    const kHalf = Math.floor(kSize / 2);

    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            let rSum = 0, gSum = 0, bSum = 0;

            for (let ky = 0; ky < kSize; ky++) {
                for (let kx = 0; kx < kSize; kx++) {
                    const px = x + kx - kHalf;
                    const py = y + ky - kHalf;
                    // 邊界像素使用 `|| {r:0,g:0,b:0}` 填補,避免超出範圍
                    const pixel = img.getPixel(px, py) || {r:0,g:0,b:0};

                    const weight = kernel[ky][kx];
                    rSum += pixel.r * weight;
                    gSum += pixel.g * weight;
                    bSum += pixel.b * weight;
                }
            }

            newImg.setPixel(x, y, Math.round(rSum), Math.round(gSum), Math.round(bSum));
        }
    }

    return newImg;
}

// 載入 BMP 影像
const img = BmpImage.load("test.bmp");
const blurredImg = convolve(img, meanKernel);
blurredImg.save("test_blur.bmp");
console.log("已生成均值模糊影像 test_blur.bmp");

說明:

  1. kernel 是卷積核矩陣
  2. 對每個像素取周圍區域加權平均
  3. 產生新影像而不修改原影像
  4. 邊界像素使用 || {r:0,g:0,b:0} 填補,避免超出範圍

2 高斯慮波

均值濾波雖然簡單,但對邊緣會造成較大模糊,這時 高斯濾波 (Gaussian Blur) 可以達到更自然的平滑效果。


2.1 高斯濾波 (Gaussian Blur) 概念

高斯濾波是將卷積核設計成 中心權重大,邊緣權重小 的矩陣。公式:

\begin{align} G(x,y) = \frac{1}{2 \pi \sigma^2} e^{-\frac{x^2 + y^2}{2\sigma^2}} \end{align}

  • $G(x,y)$ 為高斯核的值
  • $\sigma$ 控制模糊程度
  • 中心像素影響最大,越遠的像素影響越小

效果:

  • 平滑噪聲
  • 保留影像邊緣比均值濾波更自然

常見 3×3 Gaussian 核(σ≈1):

\begin{align} K = \frac{1}{16} \begin{bmatrix} 1 & 2 & 1\\ 2 & 4 & 2\\ 1 & 2 & 1 \end{bmatrix} \end{align}


2.2 JavaScript 實作高斯模糊

// Gaussian 3x3 kernel
const gaussianKernel = [
    [1/16, 2/16, 1/16],
    [2/16, 4/16, 2/16],
    [1/16, 2/16, 1/16]
];

// 呼叫 convolve 函式
const gaussianBlurredImg = convolve(img, gaussianKernel);
gaussianBlurredImg.save("test_gaussian.bmp");
console.log("已生成高斯模糊影像 test_gaussian.bmp");

說明:

  1. 核心矩陣權重總和為 1,避免亮度改變
  2. 中心像素權重最大,周圍像素權重依距離減少
  3. 使用前面自寫的 applyConvolution 函式即可
  4. 影像邊界使用相同邏輯填補,避免索引錯誤

2.3 Gaussian vs Mean

特性 均值濾波 高斯濾波
模糊效果 平滑但邊緣容易消失 平滑且邊緣更自然
計算 簡單,每個像素同權重 每個像素有不同權重
適用場景 快速降噪 保留邊緣的平滑效果

提示:高斯濾波常用於影像前處理,如邊緣檢測、降噪、特徵提取。


3 完整的應用

前面我們介紹了均值濾波與高斯濾波的概念與基本實作,這部分將示範更完整的應用,包括邊界處理與整張照片模糊效果展示。


1. 邊界處理技巧

卷積過程中,對靠近影像邊緣的像素,需要特別處理,否則可能超出索引:

  1. 零填補 (Zero Padding)

    • 在影像邊界外補 0
    • 優點:簡單
    • 缺點:邊緣會變暗
  2. 複製邊界 (Edge Replication)

    • 使用最靠近邊界的像素填充
    • 優點:保留亮度
    • 缺點:邊界略顯平滑
  3. 忽略邊界

    • 只處理內部像素
    • 優點:簡單
    • 缺點:邊緣未模糊

建議使用 複製邊界,效果自然且容易理解。


2. 自寫卷積函式(含邊界處理)


/**
 * 限制值為 0 ~ 255
 * @param {value} 輸入值
 * @returns {value} 裁剪到 0~255的值
 */
function clamp(value) {
    return Math.max(0, Math.min(255, value));
}

/**
 * 對 BMP 影像做卷積操作
 * @param {BmpImage} img - BmpImage 物件
 * @param {number[][]} kernel - 卷積核
 * @returns {BmpImage} 捲基後的新影像
 */
function convolve(img, kernel) {
    const kSize = kernel.length;
    const kHalf = Math.floor(kSize / 2);
    const newImg = BmpImage.create(img.width, img.height);

    for (let y = 0; y < img.height; y++) {
        for (let x = 0; x < img.width; x++) {
            let r = 0, g = 0, b = 0;

            for (let ky = 0; ky < kSize; ky++) {
                for (let kx = 0; kx < kSize; kx++) {
                    // 複製邊界
                    const px = Math.min(Math.max(x + kx - kHalf, 0), img.width -1);
                    const py = Math.min(Math.max(y + ky - kHalf, 0), img.height -1);

                    const pixel = img.getPixel(px, py);
                    r += pixel.r * kernel[ky][kx];
                    g += pixel.g * kernel[ky][kx];
                    b += pixel.b * kernel[ky][kx];
                }
            }
            
            // 裁剪至 0~255
            r = clamp(r);
            g = clamp(g);
            b = clamp(b);
            
            newImg.setPixel(x, y, Math.round(r), Math.round(g), Math.round(b));
        }
    }
    
    

    return newImg;
}

export convolve;

說明:

  1. Math.min(Math.max(...)) 進行 邊界複製
  2. 使用 Math.round 將結果轉為整數
  3. 支援任意奇數大小的卷積核
  4. rgb 值可能被捲基後超出0~255範圍,所以必須裁剪

3. 模糊照片示範

// 均值 3x3 kernel
const meanKernel = [
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9]
];

// Gaussian 3x3 kernel
const gaussianKernel = [
    [1/16, 2/16, 1/16],
    [2/16, 4/16, 2/16],
    [1/16, 2/16, 1/16]
];

// 載入影像
const img = BmpImage.load("test.bmp");

// 均值濾波
const meanBlur = applyConvolution(img, meanKernel);
meanBlur.save("test_mean.bmp");

// 高斯濾波
const gaussianBlur = applyConvolution(img, gaussianKernel);
gaussianBlur.save("test_gaussian.bmp");

console.log("已生成均值模糊和高斯模糊影像");

可視化效果:

  • test_mean.bmp → 全面模糊,邊緣較柔和
  • test_gaussian.bmp → 自然模糊,中心像素影響較大,細節保留較好

4. 進階思路

  1. 可調整 卷積核大小(5x5、7x7)來增加模糊程度
  2. 可自定義權重,設計特殊模糊效果(如方向性模糊)
  3. 將模糊作為影像前處理,用於 邊緣檢測、特徵提取、降噪

SNPQ © 2025