Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ColorUtil.xyToDuv #4401

Merged
merged 4 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
* @author Holger Friedrich - Transfer RGB color conversion from HSBType, improve RGB conversion, restructuring
* @author Chris Jackson - Added fromRGB (moved from HSBType)
* @author Andrew Fiddian-Green - Extensive revamp to fix bugs and improve accuracy
* @author Cody Cutrer - Added xyToDuv
*/
@NonNullByDefault
public class ColorUtil {
Expand All @@ -45,11 +46,12 @@ public class ColorUtil {
private static final BigDecimal BIG_DECIMAL_120 = BigDecimal.valueOf(120);
private static final BigDecimal BIG_DECIMAL_100 = BigDecimal.valueOf(100);
private static final BigDecimal BIG_DECIMAL_60 = BigDecimal.valueOf(60);
private static final BigDecimal BIG_DECIMAL_50 = BigDecimal.valueOf(50);
private static final BigDecimal BIG_DECIMAL_5 = BigDecimal.valueOf(5);
private static final BigDecimal BIG_DECIMAL_3 = BigDecimal.valueOf(3);
private static final BigDecimal BIG_DECIMAL_2 = BigDecimal.valueOf(2);
private static final BigDecimal BIG_DECIMAL_2_POINT_55 = new BigDecimal("2.55");
private static final double[] CORM_COEFFICIENTS = { -0.00616793, 0.0893944, -0.5179722, 1.5317403, -2.4243787,
1.925865, -0.471106 };

public static final Gamut DEFAULT_GAMUT = new Gamut(new double[] { 0.9961, 0.0001 }, new double[] { 0, 0.9961 },
new double[] { 0, 0.0001 });
Expand Down Expand Up @@ -523,6 +525,39 @@ public static HSBType xyToHsb(double[] xyY, Gamut gamut) throws IllegalArgumentE
return hsb;
}

/**
* Calculate the Duv (Delta u,v) metric from a
* <a href="https://en.wikipedia.org/wiki/CIE_1931_color_space">CIE 1931</a> {@code xy} format color.
*
* Duv describes the distance of a color point from the black body curve. It's useful for calculating
* if a color is near to "white", at any color temperature.
*
* @param xy array of double with CIE 1931 x,y in the range 0.0000 to 1.0000
* @return the calculated Duv metric
* @throws IllegalArgumentException when input array has wrong size or exceeds allowed value range.
*/
public static double xyToDuv(double[] xy) throws IllegalArgumentException {
if (xy.length != 2) {
throw new IllegalArgumentException("xyToDuv() requires 2 arguments");
}

for (int i = 0; i < xy.length; i++) {
if (xy[i] < 0 || xy[i] > 1) {
throw new IllegalArgumentException(
String.format("xyToDuv() argument %d value '%f' out of range [0..1.0]", i, xy[i]));
}
}

double x = xy[0];
double y = xy[1];
double u = 4.0 * x / (-2.0 * x + 12 * y + 3.0);
double v = 6.0 * y / (-2.0 * x + 12 * y + 3.0);
double Lfp = Math.sqrt(Math.pow(u - 0.292, 2) + Math.pow(v - 0.24, 2));
double a = Math.acos((u - 0.292) / Lfp);
double Lbb = polynomialFit(a, CORM_COEFFICIENTS);
return Lfp - Lbb;
}

/**
* Get an array of int from an array of PercentType.
*/
Expand Down Expand Up @@ -1176,4 +1211,25 @@ public static double xyToKelvin(double[] xy) {
double n = (xy[0] - 0.3320) / (0.1858 - xy[1]);
return (437 * Math.pow(n, 3)) + (3601 * Math.pow(n, 2)) + (6861 * n) + 5517;
}

/**
* Calculates a polynomial regression.
*
* This calculates the equation K[4]*x^0 + K[3]*x^1 + K[2]*x^2 + K[1]*x^3 + K[0]*x^4
*
* @param x The independent variable distributed through each term of the polynomial
* @param coefficients The coefficients of the polynomial. Note that the terms are in
* order from largest exponent to smallest exponent, which is the reverse order
* of the usual way of writing it in academic papers
* @return the result of substituting x into the regression polynomial
*/
private static double polynomialFit(double x, double[] coefficients) {
double result = 0.0;
double xAccumulator = 1.0;
for (int i = coefficients.length - 1; i >= 0; i--) {
result += coefficients[i] * xAccumulator;
xAccumulator *= x;
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,20 @@ public void testConversionRgbToHsb(int[] hsb, int[] rgb) {
assertTrue(hsbType.closeTo(new HSBType(expected), 0.01));
}

@Test
public void testXyToDuv() {
// Black
assertEquals(-0.0017d, ColorUtil.xyToDuv(new double[] { 0.3227d, 0.3290d }), 0.0001);
// 2700K
assertEquals(0.0000d, ColorUtil.xyToDuv(new double[] { 0.4599d, 0.4106d }), 0.0001);
// 3000K
assertEquals(0.0000d, ColorUtil.xyToDuv(new double[] { 0.4369d, 0.4041d }), 0.0001);
// Red
assertEquals(0.2727d, ColorUtil.xyToDuv(new double[] { 0.6987d, 0.2974d }), 0.0001);
// Yellow
assertEquals(0.0387d, ColorUtil.xyToDuv(new double[] { 0.4442d, 0.5166d }), 0.0001);
}

private void xyToXY(double[] xy, Gamut gamut) {
assertTrue(xy.length > 1);
HSBType hsb = ColorUtil.xyToHsb(xy, gamut);
Expand Down