Back
PQCHub

色彩空間轉換 - HSL 原理與應用

在前面的章節中,我們已經深入探討了 RGB 色彩空間。我們知道電腦螢幕是如何透過紅 (R)、綠 (G)、藍 (B) 三種色光的疊加來產生我們所見的千萬種顏色。我們也學會了如何讀取圖片的 RGB 數值,並進行一些基本的像素操作(例如:負片效果、轉灰階)。

然而,在實際的影像處理任務中,RGB 模型有時並不那麼好用。

這一章,我們將引入一個全新的視角來觀察顏色:HSL 色彩空間。這將是我們進行更進階影像分析(例如:特定顏色物體追蹤、調整照片氛圍、膚色檢測)的重要基石。


1. 為什麼 RGB 還不夠好?

試著想像一下,你現在是一位畫家,手裡拿著調色盤。

如果我請你:「麻煩幫我調一個亮一點的紅色。」

在現實世界中,你可能會在紅色顏料裡加一點白色。但在 RGB 的世界裡,你該怎麼做?

我們知道純紅色是 (255, 0, 0)。要讓它變「亮」,你可能需要同時增加 G 和 B 的分量,例如變成 (255, 100, 100)(這看起來是粉紅色),或者增加 R 的數值(如果還沒到 255 的話)。

再換個例子,如果我請你:「麻煩把這個綠色變得更鮮豔一點。」

在 RGB 模型中,這很難直觀地達成。你必須小心翼翼地調整三個數值的比例。

RGB 的局限性:機器友好,人類不友好

RGB 是一個面向硬體 (Hardware-oriented) 的色彩模型。它的設計初衷是為了配合顯示器(CRT、LCD、OLED)的發光原理。對於電腦來說,控制三個燈泡的亮度非常簡單直接。

但對於人類的視覺感知來說,RGB 非常不直觀。人類在描述顏色時,通常不會說:「這個顏色含有 200 單位的紅光、50 單位的綠光和 100 單位的藍光。」

我們會說:

  • 「這是一個什麼顏色?」(是紅色還是藍色?)
  • 「這個顏色有多?」(是鮮豔的大紅色,還是灰濛濛的暗紅色?)
  • 「這個顏色有多?」(是深藍色還是淺藍色?)

為了讓電腦能夠理解人類這種描述顏色的方式,科學家們設計出了更符合人類視覺感知的色彩模型,其中最著名的就是 HSL 和 HSV (或稱 HSB)。


2. 認識 HSL 色彩空間

HSL 代表了三個分量:

  • Hue (色相)
  • Saturation (飽和度)
  • Lightness (亮度)

不同於 RGB 的「立方體」模型,HSL 通常被描繪成一個圓柱體 (Cylinder)雙圓錐體 (Double Cone)

接下來,讓我們逐一拆解這三個分量:

2.1 H - Hue (色相)

色相就是我們平常所說的「顏色」本身。

在 HSL 模型中,色相被定義為一個角度,範圍是 0° 到 360°,環繞著圓柱體的中心軸。想像一個標準的色輪:

  • 0° (或 360°):純紅色 (Red)
  • 60°:黃色 (Yellow)
  • 120°:純綠色 (Green)
  • 180°:青色 (Cyan)
  • 240°:純藍色 (Blue)
  • 300°:洋紅色 (Magenta)

應用場景思考: 如果你想要在影像中把所有「紅色」的物體找出來,在 RGB 空間裡你必須寫很複雜的判斷條件(R 要很大,且 G 和 B 要很小,且要有一定的比例...)。但在 HSL 空間裡,你只需要判斷:如果 H 的值在 340° 到 20° 之間,它就是紅色系!這大大簡化了問題。

2.2 S - Saturation (飽和度)

飽和度描述了顏色的「純度」或「鮮豔程度」。

它表示從圓柱體中心軸(灰色軸)向外延伸的距離。通常用百分比 (0% - 100%) 或小數 (0.0 - 1.0) 來表示。

  • 100% (或 1.0):最外圈。表示顏色最純、最鮮豔。沒有混入白光或灰光。
  • 0% (或 0.0):最中心軸。表示完全沒有色彩,只有灰度。此時無論 H 是多少度,看起來都是灰色。

直觀理解: 飽和度降低,就像是在純色顏料中不斷加入灰色顏料,直到最後完全變成灰色。

2.3 L - Lightness (亮度)

亮度描述了顏色的明暗程度。

它對應於圓柱體的中心垂直軸,從底部的黑色到頂部的白色。同樣用百分比 (0% - 100%) 或小數 (0.0 - 1.0) 表示。

  • 0% (或 0.0):最底部。純黑色。此時無論 H 和 S 是多少,結果都是黑色。
  • 50% (或 0.5):中間點。這是展現「純色」的最佳亮度。如果 S 也是 100%,這裡就是最標準的紅、綠、藍等顏色。
  • 100% (或 1.0):最頂部。純白色。此時無論 H 和 S 是多少,結果都是白色。

注意:HSL 的 Lightness 與 HSV 的 Value 的區別 這是初學者最容易搞混的地方。在我們現在學的 HSL 模型中,亮度 100% 必定是純白色。而在另一個常見的 HSV (Value) 模型中,亮度 (Value) 100% 只代表該顏色「最亮的狀態」,不一定是白色(除非飽和度為 0)。

  • HSL 的 L=100% → 白色
  • HSL 的 L=50% → 純色 (當 S=100%)
  • HSL 的 L=0% → 黑色

3. 小結與思考

這個小時我們介紹了 HSL 色彩空間的基本概念。

  • RGB 是為了機器顯示設計的,對人類直覺不友善。
  • HSL 是為了符合人類感知設計的,將顏色拆解為色相、飽和度、亮度。
  • H (色相) 是角度,決定是什麼顏色。
  • S (飽和度) 是半徑,決定顏色有多鮮豔(多純)。
  • L (亮度) 是高度,決定顏色多亮或多暗,兩端是純黑與純白。

思考題 (非作業,僅供腦力激盪):

回想一下我們之前做過的「灰階化」處理。在 RGB 模型中,我們通常使用加權平均法 Gray = 0.299R + 0.587G + 0.114B。 如果在 HSL 模型中,一張圖片已經轉成了 HSL 格式,我們要怎麼把它變成灰階圖?需要複雜的公式嗎?還是只需要調整 H、S、L 其中一個分量就可以了?

(提示:回想一下 S 和 L 的定義,什麼情況下圖像會只剩下灰度資訊?)


沒問題,我們繼續第二個小時的課程。

再複習一次,上一個小時我們建立了 HSL 的幾何直覺:色相是角度 (H),飽和度是半徑 (S),亮度是高度 (L)。

這一小時是理論核心,我們要進入數學推導的世界。請別擔心,我們只需要用到加減乘除和一點點邏輯判斷。理解這些公式是如何設計出來的,比死背它們更重要,這能幫助你真正掌握色彩處理的精髓。


4. 從 RGB 立方體到 HSL 雙圓錐體

我們現在面臨的挑戰是:給你一個 RGB 像素值,例如 (200, 50, 50),如何算出它對應的 (H, S, L) 是多少?

這本質上是一個坐標系的轉換問題。我們要把一個定義在笛卡爾坐標系(立方體)中的點,映射到一個圓柱坐標系(雙圓錐體)中。

這個轉換過程並不是隨意的,而是基於我們對色彩感知的定義嚴格推導出來的。整個過程可以分為幾個標準步驟:歸一化、尋找極值、計算亮度、計算飽和度、最後計算色相。

讓我們開始動手算。


5. 步驟一:準備工作——歸一化 (Normalization)

RGB 的數值範圍通常是 0 到 255 (8-bit 整數)。但在進行色彩科學計算時,使用 0.0 到 1.0 的浮點數範圍會方便得多。

假設我們輸入的像素是 $(R, G, B)$。 第一步,我們將它們分別除以 255,得到歸一化後的 $(R', G', B')$:

\begin{align} R' = R / 255 \end{align}

\begin{align} G' = G / 255 \end{align}

\begin{align} B' = B / 255 \end{align}

範例: 如果輸入顏色是鮮紅色 (255, 0, 0)。 歸一化後變成: $R' = 255 / 255 = 1.0$ $G' = 0 / 255 = 0.0$ $B' = 0 / 255 = 0.0$


6. 步驟二:尋找極值與色度 (Extremes & Chroma)

接下來的幾個步驟,都依賴於這三個 $R', G', B'$ 數值中的最大值最小值

我們定義:

  • $M_{max} = \max(R', G', B')$ —— 三個分量中最亮的的那個值。
  • $M_{min} = \min(R', G', B')$ —— 三個分量中最暗的的那個值。

接著,我們計算一個非常重要的中間變數,稱為色度 (Chroma),通常用 $C$ 或 $\Delta$ 表示。它是最大值與最小值的差:

\begin{align} C = M_{max} - M_{min} \end{align}

直觀理解 $C$: 色度 $C$ 代表了這個顏色中「彩色成分」有多少。

  • 如果 $C = 0$,意味著 $M_{max} = M_{min}$,也就是 $R' = G' = B'$。回想一下上一章的思考題,當 R=G=B 時是什麼顏色?沒錯,是灰色(包括純黑和純白)。這種情況下,顏色沒有「色相」,也沒有「飽和度」。

範例: 繼續使用鮮紅色 (1.0, 0.0, 0.0)。 $M_{max} = 1.0$ (因為 R' 最大) $M_{min} = 0.0$ (因為 G' 和 B' 最小) $C = 1.0 - 0.0 = 1.0$


7. 步驟三:計算亮度 L (Lightness)

HSL 中的亮度定義非常直觀,它是最大成分和最小成分的平均值。

\begin{align} L = \frac{M_{max} + M_{min}}{2} \end{align}

這個定義確保了:

  • 純黑色 (0,0,0):$L = (0+0)/2 = 0$
  • 純白色 (1,1,1):$L = (1+1)/2 = 1$
  • 純紅色 (1,0,0):$L = (1+0)/2 = 0.5$

這符合我們上一章學到的:在 HSL 模型中,最純正的顏色位於中間亮度 (L=0.5)。

注意:與 HSV 的區別 順帶一提,在 HSV (Value) 模型中,亮度的定義比較簡單粗暴,直接就是 $V = M_{max}$。這就是為什麼 HSV 的純紅色亮度是 100%,而 HSL 的純紅色亮度是 50%。這兩個模型的根本差異就在這裡。


8. 步驟四:計算飽和度 S (Saturation)

飽和度的計算稍微複雜一點點,因為它取決於亮度 L。

回想一下 HSL 的雙圓錐體形狀。

  • 當亮度 L 接近 0 (黑色) 或接近 1 (白色) 時,圓錐體收縮成一個點,飽和度的空間變得很小,最後變成 0。
  • 當亮度 L 在中間 0.5 時,圓錐體最寬,飽和度可以達到最大。

我們分情況討論:

情況 1:如果是灰階 如果色度 $C = 0$,那麼這個顏色是灰色的。飽和度自然為 0。

\begin{align} S = 0 \quad (\text{若 } C = 0) \end{align}

情況 2:如果不是灰階 ($C > 0$) HSL 的飽和度公式如下:

\begin{align} S = \frac{C}{1 - |2L - 1|} \end{align}

這個公式看起來有點嚇人,但分母 $1 - |2L - 1|$ 的作用其實就是描述那個雙圓錐體的形狀。

  • 當 $L=0.5$ 時,分母為 $1 - |1-1| = 1$。此時 $S = C / 1 = C$。
  • 當 $L \to 0$ 或 $L \to 1$ 時,分母會趨近於 0,這個公式能確保飽和度被正確地縮放到 0-1 之間。

(註:在程式實作時,如果 L=0 或 L=1,分母會變成 0。所以通常會先判斷,如果 L=0 或 L=1,直接令 S=0,避免除以零錯誤。)

範例: 鮮紅色 (1.0, 0.0, 0.0)。已知 $C=1.0$, $L=0.5$。 $S = \frac{1.0}{1 - |2 \times 0.5 - 1|} = \frac{1.0}{1 - 0} = 1.0$ 飽和度為 100%。合理。


9. 步驟五:計算色相 H (Hue)

最後一步是計算色相角度。這是最麻煩的一步,因為我們要確定顏色落在 360 度色輪的哪個扇區。這取決於 $R', G', B'$ 哪一個是最大值 ($M_{max}$)。

情況 1:如果是灰階 ($C = 0$) 灰色沒有色相。H 的定義是未知的 (undefined)。在程式實作中,通常將其設為 0(但要記得此時 S=0,所以 H 是多少其實不影響顯示結果)。

情況 2:如果不是灰階 ($C > 0$) 我們根據哪個顏色分量主導了最大值來分段計算:

  • 如果 $M_{max} = R'$ (紅色主導,角度在 0° 或 360° 附近): 我們計算 G' 和 B' 的差異,並用 C 來標準化。 $H' = \frac{G' - B'}{C}$ (注意:如果 G' < B',這個值可能是負的,稍後會處理)

  • 如果 $M_{max} = G'$ (綠色主導,角度在 120° 附近): 我們計算 B' 和 R' 的差異。為了把角度移到 120° 附近,我們加上常數 2 (因為 2 * 60° = 120°)。 $H' = \frac{B' - R'}{C} + 2$

  • 如果 $M_{max} = B'$ (藍色主導,角度在 240° 附近): 我們計算 R' 和 G' 的差異。加上常數 4 (因為 4 * 60° = 240°)。 $H' = \frac{R' - G'}{C} + 4$

最後的轉換: 上面計算出來的 $H'$ 是一個介於 -1 到 5 之間的數值。我們需要把它轉換成 0° 到 360° 的角度。

\begin{align} H = H' \times 60^{\circ} \end{align}

處理負角度: 在紅色主導的情況下,如果 $G' < B'$,算出來的 $H$ 可能是負的(例如 -30°,代表偏紫的紅色)。為了保持在 0-360 範圍內,如果最終的 $H < 0$,我們就加上 360。

\begin{align} \text{如果 } H < 0,則 H = H + 360 \end{align}

範例: 鮮紅色 (1.0, 0.0, 0.0)。 $M_{max} = R' = 1.0$。$C=1.0$。 套用紅色主導公式: $H' = (0.0 - 0.0) / 1.0 = 0$ $H = 0 \times 60^{\circ} = 0^{\circ}$。

結果:RGB(255,0,0) 轉換為 HSL(0°, 100%, 50%)。完美符合理論!


10. 第二小時小結

恭喜你撐過了最枯燥的數學部分!

我們現在已經掌握了將任何 RGB 顏色轉換為 HSL 數值的完整演算法。這套公式雖然看起來繁瑣,但邏輯是非常嚴謹的:

  1. 歸一化到 0-1 空間。
  2. 找出最亮和最暗的分量 ($M_{max}, M_{min}$) 以及它們的差 (色度 C)。
  3. 亮度 L 是平均值。
  4. 飽和度 S 是色度 C 根據亮度 L 進行縮放的結果。
  5. 色相 H 是根據哪個顏色分量最大,來決定在色輪上的基礎角度。

思考檢查點 (Checkpoint):

試著用剛才學到的公式,在腦中(或用紙筆)快速估算一下 RGB (128, 128, 128) 的 HSL 值是多少? (提示:先算出 R', G', B',然後算出 $M_{max}$ 和 $M_{min}$,再算出 C。一旦你算出 C,答案就呼之欲出了。)


11. 實作準備:從理論到程式碼

在開始寫 code 之前,我們面臨一個實際問題:我們目前的知識是單向街。我們知道如何把 RGB 變成 HSL,但如果我們在 HSL 空間修改了顏色(例如把飽和度提高),要怎麼變回 RGB 讓螢幕顯示呢?

為了讓本章完整,我們需要補上「HSL 轉回 RGB」的拼圖。

11.1 缺失的環節:HSL 轉回 RGB (簡述)

由於時間關係,我們不進行詳細推導,而是直接給出轉換步驟(這在實務上通常也是查表或套公式)。

假設我們有 HSL 值:$H \in [0, 360)$, $S \in [0, 1]$, $L \in [0, 1]$。我們要算出 RGB (0-255)。

步驟 A:計算中間變數 C, X, m 這基本上是 RGB 轉 HSL 的逆運算。

  1. 色度 $C$ (Chroma): $C = (1 - |2L - 1|) \times S$

  2. 中間值 $X$ (用於計算次要色光分量): $X = C \times (1 - |(\frac{H}{60} \pmod 2) - 1|)$ (註:H/60 mod 2 意思是把 H 除以 60 後,取除以 2 的餘數。這能幫助我們確定在 60 度的扇區內的相對位置)

  3. 亮度匹配量 $m$: $m = L - \frac{C}{2}$

步驟 B:根據色相角度 H 決定 RGB 的基礎組合 $(R', G', B')$

我們看 H 落在六個 60° 扇區中的哪一個:

  • 0° $\le$ H < 60°: $(R', G', B') = (C, X, 0)$
  • 60° $\le$ H < 120°: $(R', G', B') = (X, C, 0)$
  • 120° $\le$ H < 180°: $(R', G', B') = (0, C, X)$
  • 180° $\le$ H < 240°: $(R', G', B') = (0, X, C)$
  • 240° $\le$ H < 300°: $(R', G', B') = (X, 0, C)$
  • 300° $\le$ H < 360°: $(R', G', B') = (C, 0, X)$

步驟 C:轉回最終的 RGB 最後,加上亮度匹配量 $m$,並乘以 255 轉回整數:

$R = (R' + m) \times 255$ $G = (G' + m) \times 255$ $B = (B' + m) \times 255$


12. 實作:核心轉換函數 (JavaScript/Node.js)

現在我們擁有了雙向轉換的能力。讓我們把它們寫成乾淨的 JavaScript 輔助函數。你可以把這些函數放在一個單獨的檔案中,例如 colorUtils.js

12.1 RGB 轉 HSL 實作

這完全是基於我們上一小時推導的公式。

/**
 * 將 RGB 顏色轉換為 HSL
 * @param {number} r - 紅色分量 (0-255)
 * @param {number} g - 綠色分量 (0-255)
 * @param {number} b - 藍色分量 (0-255)
 * @returns {object} { h, s, l } - h 在 [0, 360) 之間,s 和 l 在 [0, 1] 之間
 */
function rgbToHsl(r, g, b) {
    // 步驟 1: 歸一化
    const rPrime = r / 255;
    const gPrime = g / 255;
    const bPrime = b / 255;

    // 步驟 2: 尋找極值與色度
    const cMax = Math.max(rPrime, gPrime, bPrime);
    const cMin = Math.min(rPrime, gPrime, bPrime);
    const delta = cMax - cMin; // 即色度 C

    // 步驟 3: 計算亮度 L
    let h = 0;
    let s = 0;
    let l = (cMax + cMin) / 2;

    // 步驟 4 & 5: 計算飽和度 S 和色相 H
    if (delta === 0) {
        // 灰階情況:飽和度為 0,色相未定義 (設為 0)
        h = 0;
        s = 0;
    } 
    else {
        // 計算飽和度 S
        s = delta / (1 - Math.abs(2 * l - 1));

        // 計算色相 H
        if (cMax === rPrime) {
            h = ((gPrime - bPrime) / delta) % 6;
        } else if (cMax === gPrime) {
            h = (bPrime - rPrime) / delta + 2;
        } else {
            h = (rPrime - gPrime) / delta + 4;
        }

        h = Math.round(h * 60);

        // 處理負角度
        if (h < 0) {
            h += 360;
        }
    }

    // 保持精度,通常保留幾位小數即可,這裡為了教學清晰暫不處理
    return { h, s, l };
}

// 測試一下 (驗證上一章的課後思考題)
// console.log(rgbToHsl(128, 128, 128));
// 預期輸出: { h: 0, s: 0, l: ~0.5019... } -> 沒錯,是中灰色
// console.log(rgbToHsl(255, 0, 0));
// 預期輸出: { h: 0, s: 1, l: 0.5 } -> 純紅色

12.2 HSL 轉 RGB 實作

接下來是逆向轉換函數。

/**
 * 將 HSL 顏色轉換為 RGB
 * @param {number} h - 色相 (0-360)
 * @param {number} s - 飽和度 (0-1)
 * @param {number} l - 亮度 (0-1)
 * @returns {object} { r, g, b } - 範圍都在 0-255 之間
 */
function hslToRgb(h, s, l) {
    // 確保輸入在合理範圍
    h = h % 360;
    if (h < 0) h += 360;
    s = Math.max(0, Math.min(1, s));
    l = Math.max(0, Math.min(1, l));

    // 步驟 A: 計算中間變數 C, X, m
    const c = (1 - Math.abs(2 * l - 1)) * s;
    const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
    const m = l - c / 2;

    let rPrime = 0;
    let gPrime = 0;
    let bPrime = 0;

    // 步驟 B: 根據色相扇區決定基礎組合
    if (h >= 0 && h < 60) {
        [rPrime, gPrime, bPrime] = [c, x, 0];
    } 
    else if (h >= 60 && h < 120) {
        [rPrime, gPrime, bPrime] = [x, c, 0];
    } 
    else if (h >= 120 && h < 180) {
        [rPrime, gPrime, bPrime] = [0, c, x];
    } 
    else if (h >= 180 && h < 240) {
        [rPrime, gPrime, bPrime] = [0, x, c];
    } 
    else if (h >= 240 && h < 300) {
        [rPrime, gPrime, bPrime] = [x, 0, c];
    } 
    else {
        [rPrime, gPrime, bPrime] = [c, 0, x];
    }

    // 步驟 C: 轉回最終 RGB 整數
    const r = Math.round((rPrime + m) * 255);
    const g = Math.round((gPrime + m) * 255);
    const b = Math.round((bPrime + m) * 255);

    // 確保結果在 0-255 範圍內 (防止浮點數誤差導致越界)
    return {
        r: Math.max(0, Math.min(255, r)),
        g: Math.max(0, Math.min(255, g)),
        b: Math.max(0, Math.min(255, b))
    };
}

13. 應用範例:感受 HSL 的威力

有了這兩個核心函數,我們就可以對影像進行非常有趣的「手術」了。

假設在我們的主程式中,我們已經讀取了一張圖片的像素資料,存放在一個名為 pixelData 的 Uint8Array Buffer 中,格式為 [R, G, B, A, R, G, B, A, ...]

我們將示範兩個場景,請比較一下如果只用 RGB 模型,這些操作會有多困難。

範例一:復古風格濾鏡 (降低飽和度)

目標:我們想讓整張照片看起來有一種褪色的復古感,但又不是完全的黑白照片。 HSL 思路:這太簡單了,只需要把每個像素的 $S$ (飽和度) 乘以一個小於 1 的系數(例如 0.3),保持 H 和 L 不變。

// 假設 pixelData 是圖像的原始 Buffer 資料
// 假設 width 和 height 是圖像寬高

console.log("開始處理:復古風格濾鏡...");

for (let i = 0; i < pixelData.length; i += 4) {
    // 1. 讀取當前像素的 RGB
    const r = pixelData[i];
    const g = pixelData[i + 1];
    const b = pixelData[i + 2];
    // const a = pixelData[i + 3]; // Alpha 通道我們暫時不動

    // 2. 轉換到 HSL 空間
    let { h, s, l } = rgbToHsl(r, g, b);

    // ========== HSL 操作核心區 ==========

    // 將飽和度降低到原來的 30%
    s = s * 0.3;

    // 思考:如果這裡寫 s = 0; 會發生什麼事?
    // 答案:照片會變成完全的灰階圖!這是另一種實現灰階化的方式。

    // ====================================

    // 3. 轉回 RGB 空間
    const newRgb = hslToRgb(h, s, l);

    // 4. 寫回像素資料
    pixelData[i] = newRgb.r;
    pixelData[i + 1] = newRgb.g;
    pixelData[i + 2] = newRgb.b;
}

console.log("處理完成!");
// 接下來將 pixelData 存檔即可查看結果

範例二:魔幻改色 (色相偏移)

目標:將照片中所有的顏色進行「旋轉」。紅的變綠,綠的變藍,藍的變紅。 HSL 思路:只需調整 $H$ (色相) 角度。我們把每個像素的 $H$ 加上 120 度。

console.log("開始處理:色相偏移...");

for (let i = 0; i < pixelData.length; i += 4) {
    const r = pixelData[i];
    const g = pixelData[i + 1];
    const b = pixelData[i + 2];

    let { h, s, l } = rgbToHsl(r, g, b);

    // ========== HSL 操作核心區 ==========

    // 色相旋轉 120 度
    h = h + 120;

    // 注意:h 可能超過 360,但我們的 hslToRgb 函數裡已經處理了 % 360 的情況,
    // 所以這裡可以直接加。

    // ====================================

    const newRgb = hslToRgb(h, s, l);

    pixelData[i] = newRgb.r;
    pixelData[i + 1] = newRgb.g;
    pixelData[i + 2] = newRgb.b;
}
console.log("處理完成!");

14. 章節總結

這三個小時,我們完成了一次從硬體思維 (RGB) 到人類感知思維 (HSL) 的跨越。

  • 理論層面:我們理解了 RGB 立方體的局限性,並引入了 HSL 雙圓錐體模型,將顏色解構為色相、飽和度、亮度這三個符合直覺的分量。
  • 數學層面:我們詳細推導了從 RGB 到 HSL 的標準化和幾何轉換過程。
  • 實作層面:我們編寫了 Javascript 函數來實現雙向轉換,並看到了在 HSL 空間修改顏色是多麼的直觀和方便。

掌握色彩空間轉換,是進行進階影像處理(如電腦視覺中的物體追蹤、專業級影像調色)的關鍵一步。希望你能從這個章節中體會到數學模型與程式碼結合的樂趣!