灰階化與色彩操作
探索色彩背後的數學,用程式實作灰階、亮度、對比度演算法,掌握影像處理核心。
1 灰階化
影像處理的基礎操作中,灰階化是非常重要的一環。它可以把彩色影像轉換為只包含亮度資訊的單色影像,方便後續分析、處理或特效應用。
灰階化的核心概念是 將每個像素的紅、綠、藍三個通道 (R、G、B) 轉換為單一亮度值。但是人眼對不同顏色敏感度不同,因此直接平均 RGB 值並不總能呈現自然灰階效果。
1.1 灰階化原理
彩色影像的每個像素都由三個通道組成:
- R (Red):紅色通道
- G (Green):綠色通道
- B (Blue):藍色通道
人眼對這三種顏色的敏感度不同,綠色最敏感、紅色次之、藍色最不敏感。為了模擬自然亮度,通常使用 加權平均法 計算灰階值:
\begin{align} Gray = 0.299 \times R + 0.587 \times G + 0.114 \times B \end{align}
這種方法在攝影、電腦視覺中廣泛使用,能保留亮暗對比,讓灰階影像看起來更自然。
補充觀念:
- 如果不使用加權法,直接平均
(R+G+B)/3
會導致偏暗或偏淡,尤其在綠色占主導的場景。- 灰階化可以作為邊緣檢測、影像二值化、特徵提取的前置步驟。
1.2 程式實作
以下使用我們自製的 BmpImage 類別操作 BMP 影像,每個像素的灰階值都會依加權法計算並更新:
import BmpImage from "./BmpImage.js";
function rgbToGray(img) {
for (let y = 0; y < img.height; y++) {
for (let x = 0; x < img.width; x++) {
const { r, g, b } = img.getPixel(x, y);
const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
img.setPixel(x, y, gray, gray, gray);
}
}
}
// 載入 BMP 影像
const img = BmpImage.load("test.bmp");
// 進行灰階化
rgbToGray(img);
// 儲存結果
img.save("test_gray.bmp");
console.log("已生成灰階影像 test_gray.bmp");
說明:
getPixel(x, y)
取得每個像素的 RGB 值。BMP 格式的像素資料從左下角開始排列,BmpImage
已經內部處理 Y 軸反轉。setPixel(x, y, gray, gray, gray)
將灰階值寫回影像,R、G、B 三個通道都設成同一值,完成灰階化。- 使用
Math.round
將浮點數轉整數,確保像素值在 0–255 範圍內。
1.3 觀察結果
灰階化後的影像可以觀察到:
- 亮區與暗區對比明顯
- 細節紋理保留良好
- 對後續影像處理(如邊緣檢測、二值化)非常有幫助
- 左:原彩色 BMP
- 右:灰階化後 BMP
小技巧:對於彩色影像,也可以先灰階化再進行亮度或對比度調整,效果通常比直接操作彩色影像更穩定。
2 亮度調整
灰階化之後,我們可以對影像進行 亮度調整,控制整張影像的明暗程度。亮度調整常用於:
- 修正拍攝過暗或過亮的影像
- 增強影像細節
- 影像特效應用
2.1 亮度調整原理
亮度調整的核心概念是對每個像素的 RGB 值加上或乘上某個係數:
\begin{align} R' = clamp(R + \Delta L), \quad G' = clamp(G + \Delta L), \quad B' = clamp(B + \Delta L) \end{align}
- $\Delta L$ 為亮度增減值,可以是正數(變亮)或負數(變暗)
clamp()
函數確保結果在 0–255 之間,避免像素溢位
補充觀念:
- 直接加減亮度會保持色彩比例不變
- 也可以使用乘法 $R' = R \times factor$ 來增強對比,但需要注意溢位
2.2 程式實作
以下範例使用我們的 BmpImage 類別,對 BMP 影像進行亮度調整:
import BmpImage from "./BmpImage.js";
function clamp(value) {
return Math.max(0, Math.min(255, value));
}
function adjustBrightness(img, delta) {
for (let y = 0; y < img.height; y++) {
for (let x = 0; x < img.width; x++) {
const { r, g, b } = img.getPixel(x, y);
img.setPixel(
x,
y,
clamp(r + delta),
clamp(g + delta),
clamp(b + delta)
);
}
}
}
// 載入 BMP 影像
const img = BmpImage.load("test_gray.bmp");
// 調整亮度 (+50 增亮,-50 變暗)
adjustBrightness(img, 50);
// 儲存結果
img.save("test_bright.bmp");
console.log("已生成亮度調整影像 test_bright.bmp");
說明:
clamp
函數避免 RGB 值超出 0–255 範圍- 對每個像素同時操作 R、G、B 通道
- 可將灰階影像或彩色影像都進行亮度調整
2.3 觀察效果
- 正數增亮後,整張影像變得更明亮,暗部細節更明顯
- 負數變暗後,整張影像更沉穩,亮部細節不易曝光
小技巧:
- 先灰階化再亮度調整,可以避免彩色通道比例失衡
- 若同時要調整亮度與對比度,可以先亮度後對比度,效果更自然
3 對比度
對比度調整用來控制影像明暗差異,使亮部更亮、暗部更暗,增強影像層次感。對比度調整常用於:
- 增強影像視覺衝擊力
- 改善平淡的照片或掃描影像
- 預處理影像以利後續分析
3.1 對比度調整原理
對比度調整可以使用以下公式:
\begin{align} R' = clamp((R - 128) \times factor + 128) \end{align}
\begin{align} G' = clamp((G - 128) \times factor + 128) \end{align}
\begin{align} B' = clamp((B - 128) \times factor + 128) \end{align}
factor
:對比度係數factor > 1
增強對比度factor < 1
降低對比度
clamp()
確保像素值在 0–255 之間將 128 作為中心值,亮度值偏離中心越多,對比度越明顯
補充觀念:
- 對比度調整前可先灰階化,方便分析亮度分布
- 調整彩色影像時,RGB 通道同時調整可避免色偏
3.2 程式實作
使用 BmpImage 類別進行對比度調整:
import BmpImage from "./BmpImage.js";
function clamp(value) {
return Math.max(0, Math.min(255, value));
}
function adjustContrast(img, factor) {
for (let y = 0; y < img.height; y++) {
for (let x = 0; x < img.width; x++) {
const { r, g, b } = img.getPixel(x, y);
img.setPixel(
x,
y,
clamp((r - 128) * factor + 128),
clamp((g - 128) * factor + 128),
clamp((b - 128) * factor + 128)
);
}
}
}
// 載入 BMP 影像
const img = BmpImage.load("test_gray.bmp");
// 增加對比度 (factor > 1)
adjustContrast(img, 1.5);
// 儲存結果
img.save("test_contrast.bmp");
console.log("已生成對比度調整影像 test_contrast.bmp");
說明:
factor
控制對比度強弱- 對每個像素同時操作 R、G、B 通道
- 可配合灰階化影像,觀察效果更明顯
3.3 觀察效果
factor > 1
:亮部更亮,暗部更暗,影像層次感加強factor < 1
:整張影像平淡,對比度降低- 在部落格上展示前後對比圖,可直觀呈現對比度調整效果
小技巧:
- 調整對比度前先觀察原圖亮暗分布
- 若亮度與對比度都要調整,建議先亮度後對比度,避免亮度被過度壓縮