Манипулирование цветами в .NET – часть первая - II - Преобразование между моделями. Преобразования RGB

ОГЛАВЛЕНИЕ

II - Преобразование между моделями

Преобразования RGB

Преобразование цвета RGB в любую другую модель является основой в алгоритмах преобразования. Оно предполагает нормализацию красного, зеленого и синего: теперь значение меняется от [0..255] до [0..1].
a - RGB в HSB

Правило преобразования приведено ниже:

Ниже приведен его эквивалент на C#.

/// <summary>
/// преобразует RGB в HSB.
/// </summary>
public static HSB RGBtoHSB(int red, int green, int blue)
{
// нормализовать значения красного, зеленого и синего
double r = ((double)red/255.0);
double g = ((double)green/255.0);
double b = ((double)blue/255.0);

// начало преобразования
double max = Math.Max(r, Math.Max(g, b));
double min = Math.Min(r, Math.Min(g, b));

double h = 0.0;
if(max==r && g>=b)
{
h = 60 * (g-b)/(max-min);
}
else if(max==r && g < b)
{
h = 60 * (g-b)/(max-min) + 360;
}
else if(max == g)
{
h = 60 * (b-r)/(max-min) + 120;
}
else if(max == b)
{
h = 60 * (r-g)/(max-min) + 240;
}

double s = (max == 0)? 0.0 : (1.0 - (min/max));

return new HSB(h, s, (double)max);
}

b - RGB в HSL

Правило преобразования приведено ниже:

Эквивалент на C# следующий:

/// <summary>
/// преобразует RGB в HSL.
/// </summary>
/// <param name="red">значение красного, должно быть в [0,255].</param>
/// <param name="green">значение зеленого, должно быть в [0,255].</param>
/// <param name="blue">значение синего, должно быть в [0,255].</param>
public static HSL RGBtoHSL(int red, int green, int blue)
{
double h=0, s=0, l=0;

// нормализовать значения красного, зеленого, синего
double r = (double)red/255.0;
double g = (double)green/255.0;
double b = (double)blue/255.0;

double max = Math.Max(r, Math.Max(g, b));
double min = Math.Min(r, Math.Min(g, b));

// тон
if(max == min)
{
h = 0; // неопределённый
}
else if(max==r && g>=b)
{
h = 60.0*(g-b)/(max-min);
}
else if(max==r && g<b)
{
h = 60.0*(g-b)/(max-min) + 360.0;
}
else if(max==g)
{
h = 60.0*(b-r)/(max-min) + 120.0;
}
else if(max==b)
{
h = 60.0*(r-g)/(max-min) + 240.0;
}

// яркость
l = (max+min)/2.0;

// насыщенность
if(l == 0 || max == min)
{
s = 0;
}
else if(0<l && l<=0.5)
{
s = (max-min)/(max+min);
}
else if(l>0.5)
{
s = (max-min)/(2 - (max+min)); //(max-min > 0)?
}

return new HSL(
Double.Parse(String.Format("{0:0.##}", h)),
Double.Parse(String.Format("{0:0.##}", s)),
Double.Parse(String.Format("{0:0.##}", l))
);
}

Замечание: String.Format("{0:0.##}", h) является решением .NET для сохранения того же самого поведения округления. Если не понятно, что имеется в виду, испытайте пример кода ниже:

Console.WriteLine(Math.Round(4.45, 1)); // возвращает 4.4.
Console.WriteLine(Math.Round(4.55, 1)); // возвращает 4.6.

Проблема не заметна? Округление 4.45 должно возвращать 4.5, а не 4.4. Решение - использовать String.Format(), всегда применяющий метод "округлить до четного".

c - RGB в CMYK

Правило преобразования приведено ниже:

Эквивалент на C# следующий:

/// <summary>
/// преобразует RGB в CMYK.
/// </summary>
/// <param name="red">значение красного должно быть в [0, 255]. </param>
/// <param name="green">значение зеленого должно быть в [0, 255].</param>
/// <param name="blue">значение синего должно быть в [0, 255].</param>
public static CMYK RGBtoCMYK(int red, int green, int blue)
{
// нормализует значения красного, зеленого, синего
double c = (double)(255 - red)/255;
double m = (double)(255 - green)/255;
double y = (double)(255 - blue)/255;

double k = (double)Math.Min(c, Math.Min(m, y));

if(k == 1.0)
{
return new CMYK(0,0,0,1);
}
else
{
return new CMYK((c-k)/(1-k), (m-k)/(1-k), (y-k)/(1-k), k);
}
}

d - RGB to YUV (YUV444)

Правило преобразования приведено ниже:

Эквивалент на C# следующий:

/// <summary>
/// преобразует RGB в YUV.
/// </summary>
/// <param name="red">красный должен быть в [0, 255].</param>
/// <param name="green">зеленый должен быть в [0, 255].</param>
/// <param name="blue">синий должен быть в [0, 255].</param>
public static YUV RGBtoYUV(int red, int green, int blue)
{
YUV yuv = new YUV();

// нормализует значения красного, зеленого, синего
double r = (double)red/255.0;
double g = (double)green/255.0;
double b = (double)blue/255.0;

yuv.Y = 0.299*r + 0.587*g + 0.114*b;
yuv.U = -0.14713*r -0.28886*g + 0.436*b;
yuv.V = 0.615*r -0.51499*g -0.10001*b;

return yuv;
}

e - RGB в веб-цвет

Как известно, веб-цвета определяются двумя способами: например, "красный" определяется как rgb(255,0,0) или как #FF0000.

Объяснение второй формы простое:
• Символ "#" говорит, что формат шестнадцатеричный.
• Последние 6 символов определяют 3 пары: одну для красного, одну для зеленого и одну для синего.
• Каждая пара является шестнадцатеричным значением (основание 16) значения, изменяющегося от 0 до 255.

Можно разделить каждый компонент цвета на 16 и заменить числа, превосходящие 9, на их шестнадцатеричное значение (например, 10 = A, 11 = B и т. д.), но лучше использовать возможности String.Format().

/// <summary>
/// преобразует формат цвета RGB в шестнадцатеричный цвет.
/// </summary>
/// <param name="r">значение красного.</param>
/// <param name="g">значение зеленого.</param>
/// <param name="b">значение синего.</param>
public static string RGBToHex(int r, int g, int b)
{
return String.Format("#{0:x2}{1:x2}{2:x2}", r, g, b).ToUpper();
}

f - RGB в XYZ

Правило преобразования приведено ниже:

Эквивалент на C# следующий:

/// <summary>
/// преобразует RGB в CIE XYZ (пространство цветов CIE 1931)
/// </summary>
public static CIEXYZ RGBtoXYZ(int red, int green, int blue)
{
// нормализовать значения красного, зеленого, синего
double rLinear = (double)red/255.0;
double gLinear = (double)green/255.0;
double bLinear = (double)blue/255.0;

// преобразовать в форму sRGB
double r = (rLinear > 0.04045)? Math.Pow((rLinear + 0.055)/(
1 + 0.055), 2.2) : (rLinear/12.92) ;
double g = (gLinear > 0.04045)? Math.Pow((gLinear + 0.055)/(
1 + 0.055), 2.2) : (gLinear/12.92) ;
double b = (bLinear > 0.04045)? Math.Pow((bLinear + 0.055)/(
1 + 0.055), 2.2) : (bLinear/12.92) ;

// преобразует
return new CIEXYZ(
(r*0.4124 + g*0.3576 + b*0.1805),
(r*0.2126 + g*0.7152 + b*0.0722),
(r*0.0193 + g*0.1192 + b*0.9505)
);
}

g - RGB в L*a*b*

Преобразование в цветовую модель CIE L*a*b требует преобразования в CIE XYZ перед получением значений L*a*b*.

/// <summary>
/// преобразует RGB в CIELab.
/// </summary>
public static CIELab RGBtoLab(int red, int green, int blue)
{
return XYZtoLab( RGBtoXYZ(red, green, blue) );
}

Преобразование между XYZ и L*a*b* приведено ниже.