public static I <float> LabToXyz(this I <float> image) { if (image.Channels != 3 || (image.Format.PixelChannels != PixelChannels.Unknown && image.Format.PixelChannels != PixelChannels.Lab)) { throw new ArgumentException("LAB input image required"); } if (image.Format.ColorSpace != ColorSpace.Lab && image.Format.ColorSpace != ColorSpace.Unknown) { throw new Exception("Wrong color-space of input image. Lab expected."); } var r = CreateXyzF32(image.Height, image.Width); Range <double> r0 = r.Format.ChannelRanges[0], r1 = r.Format.ChannelRanges[1], r2 = r.Format.ChannelRanges[2]; var Xn = 0.95; var Yn = 1.0; var Zn = 1.09; for (int y = 0; y < r.Height; ++y) { for (int x = 0; x < r.Width; ++x) { var gyyn = (1.0 / 116.0) * (image[y, x, 0] + 16.0); // Calculation of color channel 1 (Y) via old channel 0 (L) r[y, x, 1] = (float)(r1.Clamp(Yn * LabToXyzFunc(gyyn))); // Calculation of color channel 0 (X) via old channels 0 (L) and 1 (a) r[y, x, 0] = (float)(r0.Clamp(Xn * LabToXyzFunc(gyyn + image[y, x, 1] / 500.0))); // Calculation of color channel 2 (Z) via old channels 0 (L) and 2 (b) r[y, x, 2] = (float)(r2.Clamp(Zn * LabToXyzFunc(gyyn - image[y, x, 2] / 200.0))); } } return(r); }
public static I <float> XyzToLab(this I <float> image) { if (image.Channels != 3 || (image.Format.PixelChannels != PixelChannels.Unknown && image.Format.PixelChannels != PixelChannels.Xyz)) { throw new ArgumentException("XYZ input image required"); } if (image.Format.ColorSpace != ColorSpace.Xyz && image.Format.ColorSpace != ColorSpace.Unknown) { throw new Exception("Wrong color-space of input image. Xyz expected."); } var r = CreateLabF32(image.Height, image.Width); Range <double> r0 = r.Format.ChannelRanges[0], r1 = r.Format.ChannelRanges[1], r2 = r.Format.ChannelRanges[2]; // For normalization of X,Y,Z to D65 white point var Xn = 0.95; var Yn = 1.0; var Zn = 1.09; for (int y = 0; y < r.Height; ++y) { for (int x = 0; x < r.Width; ++x) { // Calculation of color channel 0 (L) via old channel 1 (Y) r[y, x, 0] = (float)(r0.Clamp(116.0 * XyzToLabFunc(image[y, x, 1] / Yn) - 16.0)); // Calculation of color channel 1 (a) via old channels 0 (X) and 1 (Y) r[y, x, 1] = (float)(r1.Clamp(500.0 * (XyzToLabFunc(image[y, x, 0] / Xn) - XyzToLabFunc(image[y, x, 1] / Yn)))); // Calculation of color channel 2 (b) via old channels 1 (Y) and 2 (Z) r[y, x, 2] = (float)(r2.Clamp(200.0 * (XyzToLabFunc(image[y, x, 1] / Yn) - XyzToLabFunc(image[y, x, 2] / Zn)))); } } return(r); }
public static I <float> XyzToLuv(this I <float> image) { if (image.Channels != 3 || (image.Format.PixelChannels != PixelChannels.Unknown && image.Format.PixelChannels != PixelChannels.Xyz)) { throw new ArgumentException("XYZ input image required"); } if (image.Format.ColorSpace != ColorSpace.Xyz && image.Format.ColorSpace != ColorSpace.Unknown) { throw new Exception("Wrong color-space of input image. Xyz expected."); } var r = CreateLuvF32(image.Height, image.Width); Range <double> r0 = r.Format.ChannelRanges[0], r1 = r.Format.ChannelRanges[1], r2 = r.Format.ChannelRanges[2]; var Yn = 1.0; var Un = 0.1977; var Vn = 0.4683; var denominator = 0.0; var s = 0.0; var t = 0.0; for (int y = 0; y < r.Height; ++y) { for (int x = 0; x < r.Width; ++x) { // Calculation of color channel 0 (L) via old channel 1 (Y) if ((image[y, x, 1] / Yn) > Math.Pow((6.0 / 29.0), 3.0)) { r[y, x, 0] = (float)(r0.Clamp(116.0 * Math.Pow((image[y, x, 1] / Yn), (1.0 / 3.0)) - 16.0)); } else { r[y, x, 0] = (float)(r0.Clamp(Math.Pow((29.0 / 3.0), 3.0) * (image[y, x, 1] / Yn))); } // Calculation of auxiliary variables s and t via old channels 0 (X), 1 (Y) and 2 (Z) denominator = image[y, x, 0] + 15.0 * image[y, x, 1] + 3.0 * image[y, x, 2]; s = (4.0 * image[y, x, 0]) / denominator; t = (9.0 * image[y, x, 1]) / denominator; // Calculation of color channel 1 (u) via new channel 0 (L) and auxiliary variable s r[y, x, 1] = (float)(r1.Clamp(13.0 * r[y, x, 0] * (s - Un))); // Calculation of color channel 2 (v) via new channel 0 (L) and auxiliary variable t r[y, x, 2] = (float)(r2.Clamp(13.0 * r[y, x, 0] * (t - Vn))); } } return(r); }
public static I <float> LuvToXyz(this I <float> image) { if (image.Channels != 3 || (image.Format.PixelChannels != PixelChannels.Unknown && image.Format.PixelChannels != PixelChannels.Luv)) { throw new ArgumentException("LUV input image required"); } if (image.Format.ColorSpace != ColorSpace.Luv && image.Format.ColorSpace != ColorSpace.Unknown) { throw new Exception("Wrong color-space of input image. Luv expected."); } var r = CreateXyzF32(image.Height, image.Width); Range <double> r0 = r.Format.ChannelRanges[0], r1 = r.Format.ChannelRanges[1], r2 = r.Format.ChannelRanges[2]; var Yn = 1.0; var Un = 0.1977; var Vn = 0.4683; var s = 0.0; var t = 0.0; for (int y = 0; y < r.Height; ++y) { for (int x = 0; x < r.Width; ++x) { // Calculation of color channel 1 (Y) via old channel 0 (L) if (image[y, x, 0] > 8.0) { r[y, x, 1] = (float)(r1.Clamp(Yn * Math.Pow(((image[y, x, 0] + 16.0) / 116.0), 3.0))); } else { r[y, x, 1] = (float)(r1.Clamp(Yn * image[y, x, 0] * Math.Pow((3.0 / 29.0), 3.0))); } // Calculation of auxiliary variables s and t via old channels 0 (L), 1 (u) and 2 (v) s = image[y, x, 1] / (13.0 * image[y, x, 0]) + Un; t = image[y, x, 2] / (13.0 * image[y, x, 0]) + Vn; // Calculation of color channel 0 (X) via new channel 1 (Y) and auxiliary variables s and t r[y, x, 0] = (float)(r0.Clamp(r[y, x, 1] * ((9.0 * s) / (4.0 * t)))); // Calculation of color channel 2 (Z) via new channel 1 (Y) and auxiliary variables s and t r[y, x, 2] = (float)(r2.Clamp(r[y, x, 1] * ((12.0 - 3.0 * s - 20.0 * t) / (4.0 * t)))); } } return(r); }
public static I <float> HsvToRgb(this I <float> image) { if (image.Channels != 3 || (image.Format.PixelChannels != PixelChannels.Unknown && image.Format.PixelChannels != PixelChannels.Hsv)) { throw new ArgumentException("HSV input image required"); } if (image.Format.ColorSpace != ColorSpace.Hsv && image.Format.ColorSpace != ColorSpace.Unknown) { throw new Exception("Wrong color-space of input image. Hsv expected."); } var r = CreateRgbF32(image.Height, image.Width); Range <double> r0 = r.Format.ChannelRanges[0], r1 = r.Format.ChannelRanges[1], r2 = r.Format.ChannelRanges[2]; int h = 0; var f = 0.0; var p = 0.0; var q = 0.0; var t = 0.0; for (int y = 0; y < r.Height; ++y) { for (int x = 0; x < r.Width; ++x) { // Calculation of auxiliary variables h, f, p, q and t h = (int)(image[y, x, 0] / 60.0); f = image[y, x, 0] / 60.0 - h; p = image[y, x, 2] * (1 - image[y, x, 1]); q = image[y, x, 2] * (1 - image[y, x, 1] * f); t = image[y, x, 2] * (1 - image[y, x, 1] * (1 - f)); // Calculation of color channel 0 (R), 1 (G) and 2 (B) if (h == 0 || h == 6) { r[y, x, 0] = (float)r0.Clamp(image[y, x, 2]); r[y, x, 1] = (float)r1.Clamp(t); r[y, x, 2] = (float)r2.Clamp(p); } else if (h == 1) { r[y, x, 0] = (float)r0.Clamp(q); r[y, x, 1] = (float)r1.Clamp(image[y, x, 2]); r[y, x, 2] = (float)r2.Clamp(p); } else if (h == 2) { r[y, x, 0] = (float)r0.Clamp(p); r[y, x, 1] = (float)r1.Clamp(image[y, x, 2]); r[y, x, 2] = (float)r2.Clamp(t); } else if (h == 3) { r[y, x, 0] = (float)r0.Clamp(p); r[y, x, 1] = (float)r1.Clamp(q); r[y, x, 2] = (float)r2.Clamp(image[y, x, 2]); } else if (h == 4) { r[y, x, 0] = (float)r0.Clamp(t); r[y, x, 1] = (float)r1.Clamp(p); r[y, x, 2] = (float)r2.Clamp(image[y, x, 2]); } else if (h == 5) { r[y, x, 0] = (float)r0.Clamp(image[y, x, 2]); r[y, x, 1] = (float)r1.Clamp(p); r[y, x, 2] = (float)r2.Clamp(q); } } } return(r); }
public static I <float> RgbToHsv(this I <float> image) { if (image.Channels != 3 || (image.Format.PixelChannels != PixelChannels.Unknown && image.Format.PixelChannels != PixelChannels.Rgb)) { throw new ArgumentException("RGB input image required"); } if (image.Format.ColorSpace != ColorSpace.Srgb && image.Format.ColorSpace != ColorSpace.Unknown) { throw new Exception("Wrong color-space of input image. SRgb expected."); } var r = CreateHsvF32(image.Height, image.Width); Range <double> r0 = r.Format.ChannelRanges[0], r1 = r.Format.ChannelRanges[1], r2 = r.Format.ChannelRanges[2]; var max = 0.0; var min = 0.0; var diff = 0.0; for (int y = 0; y < r.Height; ++y) { for (int x = 0; x < r.Width; ++x) { // Calculation of max(R,G,B), min(R,G,B) and max(R,G,B) - min(R,G,B) max = Math.Max(Math.Max(image[y, x, 0], image[y, x, 1]), image[y, x, 2]); min = Math.Min(Math.Min(image[y, x, 0], image[y, x, 1]), image[y, x, 2]); diff = max - min; // Calculation of color channel 0 (H) if (max == min) { r[y, x, 0] = 0; } else if (max == image[y, x, 0]) { r[y, x, 0] = (float)(60.0 * (image[y, x, 1] - image[y, x, 2]) / diff); } else if (max == image[y, x, 1]) { r[y, x, 0] = (float)(60.0 * (2.0 + (image[y, x, 2] - image[y, x, 0]) / diff)); } else if (max == image[y, x, 2]) { r[y, x, 0] = (float)(60.0 * (4.0 + (image[y, x, 0] - image[y, x, 1]) / diff)); } if (r[y, x, 0] < 0) { r[y, x, 0] = r[y, x, 0] + 360; } r[y, x, 0] = (float)r0.Clamp(r[y, x, 0]); // Calculation of color channel 1 (S) if (max == 0) { r[y, x, 1] = 0; } else { r[y, x, 1] = (float)r1.Clamp(diff / max); } // Calculation of color channel 2 (V) r[y, x, 2] = (float)r2.Clamp(max); } } return(r); }