HSV空間; HSV Color Space
概要
HSV空間とは色空間の一つで色相(Hue)、彩度(Saturation)、明度(Value)の3変数で色を表現する。
色相は[0,6)の値を取り(文献によって異なる)、赤、黄、緑、シアン、青、マゼンダの色合いを表現する。
彩度は[0,1]の値を取り、鮮やかさを表現する。0ならば白から黒の無彩色、1ならば最も彩度が高い。
明度は[0,1]の値を取り、明るさを表現する。0ならば黒になる。
当サイトではアルファチャネルを加え透明度も考慮する。
RGBからHSVへの変換
RGB空間の色\(R, G, B\)を等価なHSV空間の色\(H, S, V\)に変換するには以下の式を用いる。
\( \quad \displaystyle C_{max} = Max(R, G, B) \)
\( \quad \displaystyle C_{min} = Min(R, G, B) \)
\( \quad \displaystyle H = \begin{cases} \displaystyle 0 &\quad C_{max} = C_{min} \\ \displaystyle \frac{G-B}{C_{max} - C_{min}} &\quad C_{max}=R ,\ G \geq B \\ \displaystyle \frac{B-R}{C_{max} - C_{min}} + 2 &\quad C_{max}=G \\ \displaystyle \frac{R-G}{C_{max} - C_{min}} + 4 &\quad C_{max}=B \\ \displaystyle \frac{G-B}{C_{max} - C_{min}} + 6 &\quad C_{max}=R ,\ G \lt B \\ \end{cases} \)
\( \quad \displaystyle S = \begin{cases} \displaystyle \frac{C_{max} - C_{min}}{C_{max}} &\quad C_{max} \gt 0 \\ \displaystyle 0 &\quad C_{max} = 0 \end{cases} \)
\( \quad \displaystyle V = C_{max} \)
HSVからRGBへの変換
HSV空間の色\(H, S, V\)を等価なRGB空間の色\(R, G, B\)に変換するには以下の式を用いる。
\(\quad \displaystyle d = \lfloor H \rfloor, \quad f = H - d \)
\( \quad \displaystyle R, G, B = \begin{cases} \displaystyle R = V, &\quad G = V (1-S(1-f)), &\quad B = V (1-S) &\quad d = 0 \\ \displaystyle R = V (1-S f), &\quad G = V, &\quad B = V (1-S) &\quad d = 1 \\ \displaystyle R = V (1-S), &\quad G = V, &\quad B = V (1-S(1-f)) &\quad d = 2 \\ \displaystyle R = V (1-S), &\quad G = V (1-S f), &\quad B = V &\quad d = 3 \\ \displaystyle R = V (1-S(1-f)), &\quad G = V (1-S), &\quad B = V &\quad d = 4 \\ \displaystyle R = V, &\quad G = V (1-S), &\quad B = V (1-S f) &\quad d = 5 \\ \end{cases} \)
ソースコード
namespace ColorField {
/// <summary>HSV空間</summary>
public unsafe struct HSV : IFormattable {
private fixed double pixel[4];
#region Constructor
/// <summary>コンストラクタ</summary>
public HSV(HSV cr) {
H = cr.H;
S = cr.S;
V = cr.V;
A = cr.A;
}
/// <summary>コンストラクタ</summary>
public HSV(double h, double s, double v, double a) {
H = h;
S = s;
V = v;
A = a;
}
/// <summary>コンストラクタ</summary>
public HSV(RGB cr) {
double max_c = Math.Max(Math.Max(cr.R, cr.G), cr.B);
double min_c = Math.Min(Math.Min(cr.R, cr.G), cr.B);
H = max_c - min_c;
S = (max_c > 0) ? (H / max_c) : 0;
V = max_c;
if(H > 0) {
if(max_c == cr.R) {
H = (cr.G - cr.B) / H + ((cr.G >= cr.B) ? 0 : 6);
}
else if(max_c == cr.G) {
H = (cr.B - cr.R) / H + 2;
}
else{
H = (cr.R - cr.G) / H + 4;
}
}
A = cr.A;
}
#endregion
#region Property
/// <summary>色相</summary>
public unsafe double H {
get {
fixed(double *pixel_ptr = pixel) {
return pixel_ptr[0];
}
}
set {
fixed(double *pixel_ptr = pixel) {
pixel_ptr[0] = value;
}
}
}
/// <summary>彩度</summary>
public unsafe double S {
get {
fixed(double *pixel_ptr = pixel) {
return pixel_ptr[1];
}
}
set {
fixed(double *pixel_ptr = pixel) {
pixel_ptr[1] = value;
}
}
}
/// <summary>明度</summary>
public unsafe double V {
get {
fixed(double *pixel_ptr = pixel) {
return pixel_ptr[2];
}
}
set {
fixed(double *pixel_ptr = pixel) {
pixel_ptr[2] = value;
}
}
}
/// <summary>透明度</summary>
public unsafe double A {
get {
fixed(double *pixel_ptr = pixel) {
return pixel_ptr[3];
}
}
set {
fixed(double *pixel_ptr = pixel) {
pixel_ptr[3] = value;
}
}
}
/// <summary>有効な領域内の値であるか判定</summary>
public bool IsValid {
get {
return (!(H < 0) && (H <= 6)) && (!(S < 0) && (S <= 1)) && (!(V < 0) && (V <= 1)) && (!(A < 0) && (A <= 1));
}
}
#endregion
#region IFormattable
/// <summary>文字列化</summary>
public override string ToString() {
return $"{H},{S},{V},{A}";
}
/// <summary>文字列化</summary>
public string ToString(string format) {
return $"{H.ToString(format)},{S.ToString(format)},{V.ToString(format)},{A.ToString(format)}";
}
/// <summary>文字列化</summary>
public string ToString(string format, IFormatProvider formatProvider) {
return ToString(format);
}
#endregion
}
}
単体テスト
namespace ColorFieldTests {
[TestClass()]
public class RGBTests {
[TestMethod()]
public void HSVRGBTest() {
RGB rgb;
rgb = new RGB(new HSV(0, 1, 1, 1));
Assert.AreEqual(rgb, new RGB(1, 0, 0, 1));
rgb = new RGB(new HSV(1, 1, 1, 1));
Assert.AreEqual(rgb, new RGB(1, 1, 0, 1));
rgb = new RGB(new HSV(2, 1, 1, 1));
Assert.AreEqual(rgb, new RGB(0, 1, 0, 1));
rgb = new RGB(new HSV(3, 1, 1, 1));
Assert.AreEqual(rgb, new RGB(0, 1, 1, 1));
rgb = new RGB(new HSV(4, 1, 1, 1));
Assert.AreEqual(rgb, new RGB(0, 0, 1, 1));
rgb = new RGB(new HSV(5, 1, 1, 1));
Assert.AreEqual(rgb, new RGB(1, 0, 1, 1));
rgb = new RGB(new HSV(6, 1, 1, 1));
Assert.AreEqual(rgb, new RGB(1, 0, 0, 1));
rgb = new RGB(new HSV(7, 1, 1, 1));
Assert.AreEqual(rgb, new RGB(1, 1, 0, 1));
rgb = new RGB(new HSV(-1, 1, 1, 1));
Assert.AreEqual(rgb, new RGB(1, 0, 1, 1));
rgb = new RGB(new HSV(-7, 1, 1, 1));
Assert.AreEqual(rgb, new RGB(1, 0, 1, 1));
}
[TestMethod()]
public void RGBHSVTest() {
HSV hsv;
hsv = new HSV(new RGB(1, 0, 0, 1));
Assert.AreEqual(hsv, new HSV(0, 1, 1, 1));
hsv = new HSV(new RGB(1, 0.5, 0, 1));
Assert.AreEqual(hsv, new HSV(0.5, 1, 1, 1));
hsv = new HSV(new RGB(1, 1, 0, 1));
Assert.AreEqual(hsv, new HSV(1, 1, 1, 1));
hsv = new HSV(new RGB(0.5, 1, 0, 1));
Assert.AreEqual(hsv, new HSV(1.5, 1, 1, 1));
hsv = new HSV(new RGB(0, 1, 0, 1));
Assert.AreEqual(hsv, new HSV(2, 1, 1, 1));
hsv = new HSV(new RGB(0, 1, 0.5, 1));
Assert.AreEqual(hsv, new HSV(2.5, 1, 1, 1));
hsv = new HSV(new RGB(0, 1, 1, 1));
Assert.AreEqual(hsv, new HSV(3, 1, 1, 1));
hsv = new HSV(new RGB(0, 0.5, 1, 1));
Assert.AreEqual(hsv, new HSV(3.5, 1, 1, 1));
hsv = new HSV(new RGB(0, 0, 1, 1));
Assert.AreEqual(hsv, new HSV(4, 1, 1, 1));
hsv = new HSV(new RGB(0.5, 0, 1, 1));
Assert.AreEqual(hsv, new HSV(4.5, 1, 1, 1));
hsv = new HSV(new RGB(1, 0, 1, 1));
Assert.AreEqual(hsv, new HSV(5, 1, 1, 1));
hsv = new HSV(new RGB(1, 0, 0.5, 1));
Assert.AreEqual(hsv, new HSV(5.5, 1, 1, 1));
hsv = new HSV(new RGB(0.5, 0.125, 0.125, 1));
Assert.AreEqual(hsv, new HSV(0, 0.75, 0.5, 1));
hsv = new HSV(new RGB(0.5, 0.5, 0.125, 1));
Assert.AreEqual(hsv, new HSV(1, 0.75, 0.5, 1));
hsv = new HSV(new RGB(0.125, 0.5, 0.125, 1));
Assert.AreEqual(hsv, new HSV(2, 0.75, 0.5, 1));
hsv = new HSV(new RGB(0.125, 0.5, 0.5, 1));
Assert.AreEqual(hsv, new HSV(3, 0.75, 0.5, 1));
hsv = new HSV(new RGB(0.125, 0.125, 0.5, 1));
Assert.AreEqual(hsv, new HSV(4, 0.75, 0.5, 1));
hsv = new HSV(new RGB(0.5, 0.125, 0.5, 1));
Assert.AreEqual(hsv, new HSV(5, 0.75, 0.5, 1));
hsv = new HSV(new RGB(1, 1, 1, 1));
Assert.AreEqual(hsv, new HSV(0, 0, 1, 1));
hsv = new HSV(new RGB(0.5, 0.5, 0.5, 1));
Assert.AreEqual(hsv, new HSV(0, 0, 0.5, 1));
hsv = new HSV(new RGB(0, 0, 0, 1));
Assert.AreEqual(hsv, new HSV(0, 0, 0, 1));
hsv = new HSV(new RGB(1.0, 0.75, 0.5, 1));
Assert.AreEqual(hsv, new HSV(0.5, 0.5, 1, 1));
}
}
}
関連項目
RGB空間