銳化與邊緣偵測
學習銳化與邊緣偵測,透過卷積核強化細節或提取輪廓,看見影像背後的神奇演算法。
1. 銳化概念
銳化 (Sharpening) 的目的在於強化影像中快速變化的像素,也就是增加邊緣或細節的對比。
1.1 理論基礎
- 銳化通常使用 高通濾波 (High-pass Filter)
- 一個常見的卷積核範例:
[ 0, -1, 0 ]
[ -1, 5, -1 ]
[ 0, -1, 0 ]
說明:
- 中間值 5:保留原始像素的主要亮度
- 四周的 -1:減去鄰近像素的平均值
- 結果:像素與鄰近像素差異越大,亮度增強,形成銳化效果
1.2 卷積計算方法
對每個像素 (x, y)
:
newPixel = sum( kernel[i][j] * pixel(x+i, y+j) )
i,j
為卷積核的座標偏移。 例如3x3卷積核 i = -1 ~ +1, j = -1 ~ +1- 對每個通道 (R, G, B) 分別計算
- 計算後需要 裁剪至 0~255,避免溢出
1.3 注意事項
邊界像素的處理:
- 可以使用 複製邊界 或 忽略邊界
對 BMP 24-bit RGB 的操作:
- 每個像素三個通道分別卷積
- 可沿用前面灰階或模糊範例的座標與索引計算方法
與前面模糊不同:
- 這次卷積核包含負值,結果可能增亮或增暗,必須注意裁剪
2. JavaScript 自製程式 — 銳化
使用前面自製的 BmpImage
類別與convolve
,可以對每個像素進行卷積運算,實作銳化效果。
2.1 使用範例
import BmpImage from './BmpImage.js';
import convolve from './convolve.js';
const img = BmpImage.load("input.bmp");
// 銳化卷積核
const sharpenKernel = [
[ 0, -1, 0 ],
[-1, 5, -1 ],
[ 0, -1, 0 ]
];
// 執行銳化
const sharpened = convolve(img, sharpenKernel);
// 儲存結果
sharpened.save("output_sharpen.bmp");
console.log("銳化完成!");
2.2 說明
convolve
函式會對每個像素套用卷積核- 邊界像素使用
|| { r:0,g:0,b:0 }
處理,避免溢出 - 計算後裁剪值到
0~255
,保持有效 RGB 範圍 - 使用
BmpImage.create
建立新的影像,避免直接覆蓋原始影像
3. 邊緣偵測 — Sobel 卷積
在影像處理中,邊緣偵測是非常重要的一環。它可以找出圖像中亮度急劇變化的位置,這些位置通常是物體的輪廓。Sobel 邊緣偵測是一種經典方法,利用 卷積 計算圖像在水平方向和垂直方向的梯度。
3.1 Sobel 原理
Sobel 邊緣偵測透過兩個卷積核分別測量水平與垂直方向的亮度變化:
- Gx(水平梯度核)
\begin{align} \begin{bmatrix} -1 & 0 & 1\\ -2 & 0 & 2\\ -1 & 0 & 1 \end{bmatrix} \end{align}
- Gy(垂直梯度核)
\begin{align} \begin{bmatrix} -1 & -2 & -1\\ 0 & 0 & 0\\ 1 & 2 & 1 \end{bmatrix} \end{align}
對每個像素計算:
\begin{align} G = \sqrt{G_x^2 + G_y^2} \end{align}
其中 $G$ 即為邊緣強度。通常會將其映射到 0~255 作為灰階值。
直觀理解:
- Gx 核會強調左右亮度變化
- Gy 核會強調上下亮度變化
- 綜合兩個方向後可以獲得完整的邊緣資訊
4.2 JavaScript 自製卷積實作
function sobelEdgeDetection(img) {
const width = img.width;
const height = img.height;
const result = BmpImage.create(width, height);
const Gx = [
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]
];
const Gy = [
[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]
];
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let gx = 0, gy = 0;
for (let ky = 0; ky < 3; ky++) {
for (let kx = 0; kx < 3; kx++) {
const px = x + kx - 1;
const py = y + ky - 1;
const pixel = img.getPixel(px, py) || { r: 0, g: 0, b: 0 };
// 先轉灰階加權法
const gray = 0.299 * pixel.r + 0.587 * pixel.g + 0.114 * pixel.b;
gx += gray * Gx[ky][kx];
gy += gray * Gy[ky][kx];
}
}
// 邊緣強度
let magnitude = Math.sqrt(gx*gx + gy*gy);
magnitude = Math.min(255, Math.round(magnitude));
result.setPixel(x, y, magnitude, magnitude, magnitude);
}
}
return result;
}
4.3 使用範例
import BmpImage from './BmpImage.js';
const img = BmpImage.load("input.bmp");
const edgeImg = sobelEdgeDetection(img);
edgeImg.save("output_edge.bmp");
console.log("Sobel 邊緣偵測完成!");
4.4 詳細解說
灰階轉換
- Sobel 計算的是亮度變化,所以先將彩色像素轉為灰階。
- 使用 加權平均法:
Gray = 0.299*R + 0.587*G + 0.114*B
,模擬人眼對色彩敏感度。
卷積運算
- 對每個像素以 3×3 卷積核掃描
- Gx 與 Gy 分別計算水平方向與垂直方向梯度
邊緣強度計算
- 使用勾股定理:
sqrt(Gx^2 + Gy^2)
- 將結果裁剪到 0~255
- 使用勾股定理:
結果影像
- 生成灰階影像,亮的部分即為邊緣
- 可直接保存為 BMP 觀察效果
小結: Sobel 卷積將卷積與灰階概念結合,不僅可偵測邊緣,還可作為後續影像處理演算法的基礎,如輪廓辨識、物體檢測或馬賽克處理前的區域識別。