mirror of
https://github.com/Maassoft/ColorControl.git
synced 2026-10-22 00:22:18 -05:00
180 lines
5.6 KiB
C#
180 lines
5.6 KiB
C#
// credit to https://mina86.com/2019/srgb-xyz-matrix/ and http://www.brucelindbloom.com/ for the math
|
|
|
|
namespace novideo_srgb
|
|
{
|
|
public static class Colorimetry
|
|
{
|
|
public struct Point
|
|
{
|
|
public bool Equals(Point other)
|
|
{
|
|
return X.Equals(other.X) && Y.Equals(other.Y);
|
|
}
|
|
|
|
public override bool Equals(object? obj)
|
|
{
|
|
return obj is Point other && Equals(other);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
unchecked
|
|
{
|
|
return (X.GetHashCode() * 397) ^ Y.GetHashCode();
|
|
}
|
|
}
|
|
|
|
public double X;
|
|
public double Y;
|
|
}
|
|
|
|
public struct ColorSpace
|
|
{
|
|
public bool Equals(ColorSpace other)
|
|
{
|
|
return Red.Equals(other.Red) && Green.Equals(other.Green) && Blue.Equals(other.Blue) &&
|
|
White.Equals(other.White);
|
|
}
|
|
|
|
public override bool Equals(object? obj)
|
|
{
|
|
return obj is ColorSpace other && Equals(other);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
unchecked
|
|
{
|
|
var hashCode = Red.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ Green.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ Blue.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ White.GetHashCode();
|
|
return hashCode;
|
|
}
|
|
}
|
|
|
|
public Point Red;
|
|
public Point Green;
|
|
public Point Blue;
|
|
public Point White;
|
|
}
|
|
|
|
public static Point D65 = new Point { X = 0.3127, Y = 0.3290 };
|
|
|
|
public static ColorSpace sRGB = new ColorSpace
|
|
{
|
|
Red = new Point { X = 0.64, Y = 0.33 },
|
|
Green = new Point { X = 0.3, Y = 0.6 },
|
|
Blue = new Point { X = 0.15, Y = 0.06 },
|
|
White = D65
|
|
};
|
|
|
|
public static ColorSpace DisplayP3 = new ColorSpace
|
|
{
|
|
Red = new Point { X = 0.68, Y = 0.32 },
|
|
Green = new Point { X = 0.265, Y = 0.69 },
|
|
Blue = new Point { X = 0.15, Y = 0.06 },
|
|
White = D65
|
|
};
|
|
|
|
public static ColorSpace AdobeRGB = new ColorSpace
|
|
{
|
|
Red = new Point { X = 0.64, Y = 0.33 },
|
|
Green = new Point { X = 0.21, Y = 0.71 },
|
|
Blue = new Point { X = 0.15, Y = 0.06 },
|
|
White = D65
|
|
};
|
|
|
|
public static ColorSpace BT2020 = new ColorSpace
|
|
{
|
|
Red = new Point { X = 0.708, Y = 0.292 },
|
|
Green = new Point { X = 0.17, Y = 0.797 },
|
|
Blue = new Point { X = 0.131, Y = 0.046 },
|
|
White = D65
|
|
};
|
|
|
|
public static ColorSpace[] ColorSpaces => new[] { sRGB, DisplayP3, AdobeRGB, BT2020 };
|
|
|
|
public static Matrix D50 = Matrix.FromValues(new[,] { { 0.9642 }, { 1 }, { 0.8249 } });
|
|
|
|
public static Matrix RGBToXYZ(ColorSpace colorSpace)
|
|
{
|
|
var red = colorSpace.Red;
|
|
var green = colorSpace.Green;
|
|
var blue = colorSpace.Blue;
|
|
var white = colorSpace.White;
|
|
var whiteXYZ = Matrix.FromValues(new[,]
|
|
{ { white.X / white.Y }, { 1 }, { (1 - white.X - white.Y) / white.Y } });
|
|
|
|
var Mprime = Matrix.FromValues(new[,]
|
|
{
|
|
{ red.X / red.Y, green.X / green.Y, blue.X / blue.Y },
|
|
{ 1, 1, 1 },
|
|
{ (1 - red.X - red.Y) / red.Y, (1 - green.X - green.Y) / green.Y, (1 - blue.X - blue.Y) / blue.Y }
|
|
});
|
|
|
|
return Mprime * Matrix.FromDiagonal(Mprime.Inverse() * whiteXYZ);
|
|
}
|
|
|
|
public static Matrix XYZToRGB(ColorSpace colorSpace)
|
|
{
|
|
return RGBToXYZ(colorSpace).Inverse();
|
|
}
|
|
|
|
public static Matrix RGBToRGB(ColorSpace from, ColorSpace to)
|
|
{
|
|
var result = XYZToRGB(to) * RGBToXYZ(from);
|
|
return result;
|
|
}
|
|
|
|
public static Matrix RGBToAdaptedXYZ(ColorSpace colorspace, Matrix whiteXYZ)
|
|
{
|
|
var xyz = RGBToXYZ(colorspace);
|
|
var bradford = Matrix.FromValues(new[,]
|
|
{
|
|
{ 0.8951, 0.2664, -0.1614 },
|
|
{ -0.7502, 1.7135, 0.0367 },
|
|
{ 0.0389, -0.0685, 1.0296 }
|
|
});
|
|
var ws = colorspace.White;
|
|
var aws = bradford * Matrix.FromValues(new[,]
|
|
{
|
|
{ ws.X / ws.Y }, { 1 }, { (1 - ws.X - ws.Y) / ws.Y }
|
|
});
|
|
var awd = bradford * whiteXYZ;
|
|
var m = bradford.Inverse() * Matrix.FromDiagonal(new[]
|
|
{ awd[0] / aws[0], awd[1] / aws[1], awd[2] / aws[2] }) * bradford;
|
|
return m * xyz;
|
|
}
|
|
|
|
public static Matrix RGBToPCSXYZ(ColorSpace colorspace)
|
|
{
|
|
return RGBToAdaptedXYZ(colorspace, D50);
|
|
}
|
|
|
|
public static Matrix PCSXYZToRGB(ColorSpace colorspace)
|
|
{
|
|
return RGBToPCSXYZ(colorspace).Inverse();
|
|
}
|
|
|
|
public static Matrix XYZScale(Matrix matrix, Matrix target)
|
|
{
|
|
var result = Matrix.Zero3x3();
|
|
var white = matrix * Matrix.One3x1();
|
|
for (var i = 0; i < 3; i++)
|
|
{
|
|
for (var j = 0; j < 3; j++)
|
|
{
|
|
result[i, j] = matrix[i, j] * target[i] / white[i];
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static Matrix XYZScaleToD50(Matrix matrix)
|
|
{
|
|
return XYZScale(matrix, D50);
|
|
}
|
|
}
|
|
} |