平滑濾波與卷積
深入卷積概念,用程式實作均值與高斯濾波,掌握影像平滑技術,創造專業級模糊效果。
影像平滑是影像處理中最常見的操作之一,通常用於:
- 降低影像噪聲
- 模糊影像以突出整體結構
- 預處理以利邊緣檢測或其他特徵提取
1 基本概念
1.1 卷積 (Convolution) 概念
卷積是數位影像處理的核心操作。簡單說,卷積就是:
- 選擇一個小矩陣(稱為 卷積核 或 kernel),例如 3×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");
說明:
kernel
是卷積核矩陣- 對每個像素取周圍區域加權平均
- 產生新影像而不修改原影像
- 邊界像素使用
|| {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,避免亮度改變
- 中心像素權重最大,周圍像素權重依距離減少
- 使用前面自寫的
applyConvolution
函式即可 - 影像邊界使用相同邏輯填補,避免索引錯誤
2.3 Gaussian vs Mean
特性 | 均值濾波 | 高斯濾波 |
---|---|---|
模糊效果 | 平滑但邊緣容易消失 | 平滑且邊緣更自然 |
計算 | 簡單,每個像素同權重 | 每個像素有不同權重 |
適用場景 | 快速降噪 | 保留邊緣的平滑效果 |
提示:高斯濾波常用於影像前處理,如邊緣檢測、降噪、特徵提取。
3 完整的應用
前面我們介紹了均值濾波與高斯濾波的概念與基本實作,這部分將示範更完整的應用,包括邊界處理與整張照片模糊效果展示。
1. 邊界處理技巧
卷積過程中,對靠近影像邊緣的像素,需要特別處理,否則可能超出索引:
零填補 (Zero Padding)
- 在影像邊界外補 0
- 優點:簡單
- 缺點:邊緣會變暗
複製邊界 (Edge Replication)
- 使用最靠近邊界的像素填充
- 優點:保留亮度
- 缺點:邊界略顯平滑
忽略邊界
- 只處理內部像素
- 優點:簡單
- 缺點:邊緣未模糊
建議使用 複製邊界,效果自然且容易理解。
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;
說明:
Math.min(Math.max(...))
進行 邊界複製- 使用
Math.round
將結果轉為整數 - 支援任意奇數大小的卷積核
- 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. 進階思路
- 可調整 卷積核大小(5x5、7x7)來增加模糊程度
- 可自定義權重,設計特殊模糊效果(如方向性模糊)
- 將模糊作為影像前處理,用於 邊緣檢測、特徵提取、降噪