在 C++ 中,8 位元 RGB 轉 HSV 轉換器會將 R、G 和 B 除以 255.0 進行正規化,接著將最大值作為 value,當最大值不為零時,以 (maximum - minimum) / maximum 計算 saturation,並從最大值所在的通道計算 hue。 下列函式會回傳以度數表示的 hue,其範圍從 0 到不含 360,以及以 0 到 1 的比例表示的 saturation 和 value,因此可重複用於採用不同輸出慣例的應用程式。

定義 RGB 與 HSV 的介面契約
8 位元 C++ 介面應接受介於 0 到 255 之間的整數紅色、綠色和藍色通道。HSV 並不會取代或近似 RGB 三元組;它會重新組織相同的顏色,讓 hue、色彩強度和亮度能分別處理。
| 元件 | 計算方式 | 範圍 | 意義 |
|---|---|---|---|
| Hue | 使用最大值所在 RGB 通道的分支,並在負角度加上 360 以進行循環處理 | 0–360 度 | 在色輪上的位置 |
| Saturation | delta / maximum,並為黑色另行設定零值結果 | 0–1 | 相對於 value,與灰階軸的距離 |
| Value | 正規化後 RGB 通道中的最大值 | 0–1 | 最強通道的強度 |
在此,delta 是正規化後最大值與最小值之間的差異。當三個 RGB 通道完全相同時,delta 會變成零,因此必須明確採用無彩色慣例。
建立完整的 C++ 轉換器
此實作僅使用 C++ 標準函式庫,並將計算所得的值以雙精度浮點數保留至顯示時才處理。
- 將紅色、綠色和藍色接受為整數參數,並拒絕 0–255 範圍外的值。
- 將每個通道除以 255.0 進行正規化,接著找出最大值、最小值和 delta。
- 計算 value 和 saturation,並處理最大值為零的黑色情況。
- 從最大值所在的通道計算 hue,並在中期 hue 為負值時加上 360。
- 回傳未經四捨五入的結果,僅在寫入輸出時格式化。
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <stdexcept>
struct HSV {
double h; // degrees in [0, 360)
double s; // fraction in [0, 1]
double v; // fraction in [0, 1]
};
HSV rgbToHsv(int red, int green, int blue) {
if (red < 0 || red > 255 ||
green < 0 || green > 255 ||
blue < 0 || blue > 255) {
throw std::invalid_argument("Each RGB channel must be an integer from 0 to 255");
}
const double r = red / 255.0;
const double g = green / 255.0;
const double b = blue / 255.0;
const double maxChannel = std::max({r, g, b});
const double minChannel = std::min({r, g, b});
const double delta = maxChannel - minChannel;
const double value = maxChannel;
const double saturation = maxChannel == 0.0
? 0.0
: delta / maxChannel;
double hue = 0.0;
if (delta > 0.0) {
if (maxChannel == r) {
hue = 60.0 * ((g - b) / delta);
} else if (maxChannel == g) {
hue = 60.0 * ((b - r) / delta + 2.0);
} else {
hue = 60.0 * ((r - g) / delta + 4.0);
}
if (hue < 0.0) {
hue += 360.0;
}
}
return {hue, saturation, value};
}
int main() {
const HSV hsv = rgbToHsv(32, 128, 255);
std::cout << std::fixed << std::setprecision(2)
<< "H = " << hsv.h << " degrees\n"
<< "S = " << hsv.s * 100.0 << "%\n"
<< "V = " << hsv.v * 100.0 << "%\n";
return 0;
}
使用 255.0 很重要,因為將兩個整數變數除以整數 255,會截斷每個正規化後的通道。delta 檢查也能在不必除以零的情況下處理無彩色。雖然範例程式會將結果格式化為小數點後兩位,但回傳的 double 值仍保持未四捨五入。
透過一次計算檢查結果
使用 RGB (32, 128, 255) 進行一次端到端檢查。正規化後的值為 r = 32/255、g = 128/255 和 b = 1。因此,最大值為 1,最小值為 32/255,delta 為 223/255。代入後可得 V = 1,S = (223/255) / 1 = 0.8745098。
由於藍色是最大值所在的通道,hue 公式為 H = 240 + 60((r - g) / delta)。代入相同的值可得 H = 240 + 60((32 - 128) / (255 - 32)) = 214.1704。因此,最終顯示結果為 H = 214.17°、S = 87.45% 和 V = 100.00%。
符合目標端所預期的尺度
HSV 座標若沒有單位,資訊就不完整。即使結果在數學上正確,當程式庫預期的是緊湊的整數範圍,而非度數與比例時,結果仍可能被拒絕或誤讀。
| 目標端慣例 | Hue | Saturation 和 value | C++ 處理方式 |
|---|---|---|---|
| 數學或 Android 風格 API | 度數,至少為 0 且低於 360 | 0 到 1 的比例 | 直接回傳 struct 的值 |
| 以百分比為主的介面 | 度數 | 0 到 100 的百分比 | 顯示時將比例乘以 100 |
| 緊湊的 8 位元影像處理流程 | 遵循目標文件 | 遵循目標文件 | 轉換後套用文件中記載的縮放方式 |
Android Color.colorToHSV 文件將 hue 定義為非負且低於 360 的度數範圍,並將 saturation 和 value 定義為 0 到 1 的值,與上述回傳的結構相符。
OpenCV 需要特別注意,因為其常見的 8 位元 HSV 輸出採用 0 到 179 的 hue,以及 0 到 255 的 saturation 和 value,而影像通道的順序也可能是 BGR,而非 RGB。套用封裝轉換前,請遵循專門的 OpenCV HSV 尺度指南。
處理無彩色,不要憑空建立 hue
當紅色、綠色和藍色相同時,該顏色不含可見的 hue:saturation 為 0,而 hue 座標沒有視覺意義。此函式會將 hue 保留為慣用的預留值 0 度,而不是除以零的 delta。黑色的 saturation 也為 0,但其 value 為 0,因為所有正規化後的通道都是零。
對於灰色,saturation 仍為 0,而 value 則反映各通道共有的強度。預留的 hue 不會還原任何先前的顏色資訊,因此應用程式不應將其解讀為色輪上的有效位置。
在瀏覽器中驗證相同的三元組
若要進行無程式碼的交叉檢查,RGB 轉 HSV 轉換器可接受相同的 8 位元整數三元組,並將轉換後的座標與即時色塊一併顯示。
- 輸入介於 0 到 255 之間的整數紅色、綠色和藍色通道值。空白欄位、小數值、負數以及高於 255 的值,都會產生明確錯誤,而不會在未提示的情況下遭到四捨五入或限制在範圍內。
- 選擇 Convert to HSV,然後讀取以度數表示的 hue,以及以百分比表示的 saturation 和 value。該頁面也會顯示正規化的 HSV 表示法、原始 HEX 色碼和視覺色塊。
- 複製數值前,請先確認目標端預期的是度數與百分比、0 到 1 的比例,還是緊湊的整數範圍。
瀏覽器介面會在本地計算,保留輸入值供比較,而且不會儲存色彩歷史。它為便於閱讀而顯示小數點後兩位,但底層計算仍保留未經四捨五入的浮點數值。您的 C++ 函式應遵循相同原則,將最終四捨五入留給輸出邊界處理。
遵守 RGB 轉 HSV 的界線
座標轉換解決的是數值表示問題,而非所有色彩管理或設計問題:
- HSV value 不等於 HSL lightness。 HSV value 是正規化後 RGB 通道中的最大值,而 HSL lightness 使用最大值與最小值通道的平均值。兩者都不應視為實測的感知亮度。
- Alpha 不在轉換範圍內。 透明度不會改變底下三個通道的 RGB 轉 HSV 計算。
- 輸入為 sRGB 風格的 8 位元三元組。 轉換器不會讀取內嵌的影像描述檔,也不會在廣色域色彩空間之間進行轉換。
- 不同目標端的四捨五入方式可能不同。 在 C++ 計算過程中保留完整精度,然後套用接收端程式庫文件中記載的尺度和四捨五入規則。
- 轉換無法確保可讀性。 若真正目標是取得無障礙的前景與背景顏色,請使用 色彩對比檢查器測試實際的顏色配對。