openhab/openhab-core

ColorUtils.hsbToXy() has errors when B part is small

Closed this issue · 2 comments

Issue

The ColorUtils.hsbToxy() method seems to have very high errors when the HSB B part is very small. This is probably due to rounding errors. The reason is that hsbToXy internally uses hsbToRgb() and that latter method scales its output depending on the B value.

However since the output of hsbToXy() is simply a point on the CIE color space, in reality only the HS part of the HSB is necessary to determine that point in xy space.

Proposed solution

The ColorUtils.hsbToxy() method should normalise the B part to 100% so that the HS part calculates the respective XY point in the CIE color space with maximum precision.

This means that hsbToXy will consistently produce the same xy color space point for the full 0..100% range of the B part.

To Do

Also check if the counterpart ColorUtils.xyToHsb method suffers the same problem..

Example

Below is a listing of the output of hsbToXy() for various B values between 0 and 100. My expectation is that the x and y values should NOT vary depending on the B value..

HSB[39.000, 40.000, 0.000] => xyY[0.000, 0.000, 0.000]
HSB[39.000, 40.000, 0.100] => xyY[36.720, 36.430, 0.000]
HSB[39.000, 40.000, 0.200] => xyY[36.720, 36.430, 0.010]
HSB[39.000, 40.000, 0.300] => xyY[36.720, 36.430, 0.020]
HSB[39.000, 40.000, 0.400] => xyY[36.720, 36.430, 0.020]
HSB[39.000, 40.000, 0.500] => xyY[36.720, 36.430, 0.030]
HSB[39.000, 40.000, 0.600] => xyY[36.720, 36.430, 0.040]
HSB[39.000, 40.000, 0.700] => xyY[36.720, 36.430, 0.040]
HSB[39.000, 40.000, 0.800] => xyY[36.720, 36.430, 0.050]
HSB[39.000, 40.000, 0.900] => xyY[36.720, 36.430, 0.060]
HSB[39.000, 40.000, 1.000] => xyY[36.720, 36.430, 0.060]
HSB[39.000, 40.000, 1.100] => xyY[36.720, 36.430, 0.070]
HSB[39.000, 40.000, 1.200] => xyY[36.720, 36.430, 0.080]
HSB[39.000, 40.000, 1.300] => xyY[36.720, 36.430, 0.080]
HSB[39.000, 40.000, 1.400] => xyY[36.720, 36.430, 0.090]
HSB[39.000, 40.000, 1.500] => xyY[36.720, 36.430, 0.100]
HSB[39.000, 40.000, 1.600] => xyY[36.720, 36.430, 0.100]
HSB[39.000, 40.000, 1.700] => xyY[36.720, 36.430, 0.110]
HSB[39.000, 40.000, 1.800] => xyY[36.720, 36.430, 0.120]
HSB[39.000, 40.000, 1.900] => xyY[36.720, 36.430, 0.130]
HSB[39.000, 40.000, 2.000] => xyY[36.720, 36.430, 0.130]
HSB[39.000, 40.000, 2.100] => xyY[36.720, 36.430, 0.140]
HSB[39.000, 40.000, 2.200] => xyY[36.720, 36.430, 0.150]
HSB[39.000, 40.000, 2.300] => xyY[36.720, 36.430, 0.150]
HSB[39.000, 40.000, 2.400] => xyY[36.720, 36.430, 0.160]
HSB[39.000, 40.000, 2.500] => xyY[36.720, 36.430, 0.170]
HSB[39.000, 40.000, 2.600] => xyY[36.720, 36.430, 0.170]
HSB[39.000, 40.000, 2.700] => xyY[36.720, 36.430, 0.180]
HSB[39.000, 40.000, 2.800] => xyY[36.720, 36.430, 0.190]
HSB[39.000, 40.000, 2.900] => xyY[36.720, 36.430, 0.190]
HSB[39.000, 40.000, 3.000] => xyY[36.720, 36.430, 0.200]
HSB[39.000, 40.000, 3.100] => xyY[36.720, 36.430, 0.210]
HSB[39.000, 40.000, 3.200] => xyY[36.720, 36.430, 0.210]
HSB[39.000, 40.000, 3.300] => xyY[36.720, 36.430, 0.220]
HSB[39.000, 40.000, 3.400] => xyY[36.720, 36.430, 0.230]
HSB[39.000, 40.000, 3.500] => xyY[36.720, 36.430, 0.240]
HSB[39.000, 40.000, 3.600] => xyY[36.720, 36.430, 0.240]
HSB[39.000, 40.000, 3.700] => xyY[36.720, 36.430, 0.250]
HSB[39.000, 40.000, 3.800] => xyY[36.720, 36.430, 0.260]
HSB[39.000, 40.000, 3.900] => xyY[36.720, 36.430, 0.260]
HSB[39.000, 40.000, 4.000] => xyY[36.720, 36.430, 0.270]
HSB[39.000, 40.000, 4.100] => xyY[36.720, 36.430, 0.280]
HSB[39.000, 40.000, 4.200] => xyY[36.730, 36.420, 0.280]
HSB[39.000, 40.000, 4.300] => xyY[36.750, 36.420, 0.290]
HSB[39.000, 40.000, 4.400] => xyY[36.760, 36.420, 0.300]
HSB[39.000, 40.000, 4.500] => xyY[36.780, 36.410, 0.300]
HSB[39.000, 40.000, 4.600] => xyY[36.810, 36.410, 0.310]
HSB[39.000, 40.000, 4.700] => xyY[36.840, 36.400, 0.320]
HSB[39.000, 40.000, 4.800] => xyY[36.870, 36.400, 0.330]
HSB[39.000, 40.000, 4.900] => xyY[36.890, 36.400, 0.330]
HSB[39.000, 40.000, 5.000] => xyY[36.920, 36.410, 0.340]
HSB[39.000, 40.000, 10.000] => xyY[38.580, 37.390, 0.840]
HSB[39.000, 40.000, 15.000] => xyY[39.540, 37.980, 1.600]
HSB[39.000, 40.000, 20.000] => xyY[40.130, 38.330, 2.670]
HSB[39.000, 40.000, 25.000] => xyY[40.530, 38.560, 4.070]
HSB[39.000, 40.000, 30.000] => xyY[40.820, 38.730, 5.820]
HSB[39.000, 40.000, 35.000] => xyY[41.040, 38.850, 7.950]
HSB[39.000, 40.000, 40.000] => xyY[41.220, 38.950, 10.470]
HSB[39.000, 40.000, 45.000] => xyY[41.350, 39.020, 13.410]
HSB[39.000, 40.000, 50.000] => xyY[41.470, 39.080, 16.770]
HSB[39.000, 40.000, 55.000] => xyY[41.560, 39.140, 20.590]
HSB[39.000, 40.000, 60.000] => xyY[41.640, 39.180, 24.860]
HSB[39.000, 40.000, 65.000] => xyY[41.710, 39.220, 29.620]
HSB[39.000, 40.000, 70.000] => xyY[41.770, 39.250, 34.860]
HSB[39.000, 40.000, 75.000] => xyY[41.820, 39.280, 40.620]
HSB[39.000, 40.000, 80.000] => xyY[41.870, 39.300, 46.890]
HSB[39.000, 40.000, 85.000] => xyY[41.910, 39.320, 53.690]
HSB[39.000, 40.000, 90.000] => xyY[41.950, 39.340, 61.030]
HSB[39.000, 40.000, 95.000] => xyY[41.980, 39.360, 68.930]
HSB[39.000, 40.000,100.000] => xyY[42.010, 39.380, 77.400]

I think this behavior is due to Gamma Correction. So it is actually as intended. However the reason why I noticed it, is due to round trip inconsistencies in the Hue binding, where reverse gamma correction may not be being properly applied. I.e. perhaps a bug in the Hue binding.

The variation of xy values depending on brightness is -- correctly -- due to the Gamma correction. Therefore the topic reported here is not an issue. The discrepancies observed here were discovered during debugging an issue in the OH Hue binding. And that issue will be resolved via openhab/openhab-addons#16436. So therefore I am closing this issue now.