Closed shawnrice closed 10 years ago
In order to test this in the other versions, could you explain more precisely how to verify if it's working correctly?
If I convert hex1->rbg->hsv->rgb->hex2
and hex1 == hex2
, does that mean it's correct?
Yes. Well,
hex1==hex2
rgb1==rgb2
But Hex
is the most important.
I also think that the conversion functions are a bit easier to read now, especially when in the color_conversion_tests.php
file.
Also, look at the luminance
function: I think it's a bit more of an elegant standard to use to test light/dark than the older one.
The initial implementation of luminance was similar, but this returns a nice value between 0 and 1 where .5 is the neutral point. So, the function is (r(.39) + g(.59) + b(.11)) / 255
.
So, the luminance test for the icon should simply be:
$rgb = $this->rgb_to_hex( $hex );
if ( ( ( (rgb['r'] * .39) + (rgb['g'] * .59) + (rgb['b'] * .11) ) / 255 ) > .5 )
return 'light';
else
return 'dark';
The colors are weighted in how the human eye perceives light. Green light is "brighter" than blue light, brighter in that the human eye picks it up better. Cool, right? If you want to test this out for experience, then create an HTML document:
<html style='padding:0;'>
<body style='padding:0; margin:0'>
<div style='background-color: rgb(255, 0, 0); width: 100%; height: 100%'>red</div>
<div style='background-color: rgb(0, 255, 0); width: 100%; height: 100%'>green;</div>
<div style='background-color: rgb(0, 0, 255); width: 100%; height: 100%'>blue</div>
</body>
</html>
Then, open it in a browser, go in a dark room, and scroll through them, feeling how your eyes adjust/react. It's an interesting feeling.
The values won't round-trip properly. Sometimes they're just a little off, due to rounding errors between int
and float
, but other times they're a fair way off.
I'm guessing this is because the conversion is lossy. What's your experience?
The first algorithm made it so that they never came out the same. Close, but not the same.
With the new one, they came out differently only once, and then I adjusted it a tiny bit (round instead of floor) and haven't seen it come out differently since. (Although, I've run the test script through only about 30 values, so, by no means exhaustive).
I think that at least some of the improved accuracy this time comes from that I'm not using floor
. Also, the best discovery was that the %
operator in PHP didn't play so well with floats.
And, there will be loss, but I think this new one makes it less lossy. Anytime we're converting from a ten decimal float to an integer will have some loss (mostly).
How does round()
work in PHP?
I was testing it with a function that generates random colours. In some instances, the round-tripped values were off by 30-40, which isn't a rounding error.
Or that might have been the colours round-tripped through alter
.
While unit testing, I just ran this code:
$color = generate_hex();
$hex1 = $i->color( $color );
$rgb1 = $i->hexToRgb( $hex1 );
$hsv = $i->rgbToHsv( $rgb1 );
$rgb2 = $i->hsvToRgb( $hsv );
$hex2 = $i->rgbToHex($rgb2);
if ( $hex1 == $hex2 ) {
echo "$hex1 == $hex2" . PHP_EOL;
return TRUE;
}
$i
is an AlfredBundlerIcon
object instantiated on its own. generate_hex()
, well, generates a random hex. And I ran it on a loop 100,000
times. Results:
.... (truncated) ....
Calling color inverse test 99988: 6bc37f == 6bc37f
Calling color inverse test 99989: e61667 == e61667
Calling color inverse test 99990: 715442 == 715442
Calling color inverse test 99991: 77ec02 == 77ec02
Calling color inverse test 99992: 992dfb == 992dfb
Calling color inverse test 99993: d2bf60 == d2bf60
Calling color inverse test 99994: 4389fa == 4389fa
Calling color inverse test 99995: 2751bb == 2751bb
Calling color inverse test 99996: 8be9c4 == 8be9c4
Calling color inverse test 99997: 46fe3e == 46fe3e
Calling color inverse test 99998: c99162 == c99162
Calling color inverse test 99999: 2171c6 == 2171c6
Calling color inverse test 100000: bfca60 == bfca60
======================================================
Completed 100000 tests. For 100000 tests, we passed 100000 (100%)
So... it seems like the PHP version is pretty good.
The only time I rounded was at the end of hsvToRgb
:
$r = round( ( $r + $min ) * 255 );
$g = round( ( $g + $min ) * 255 );
$b = round( ( $b + $min ) * 255 );
PHP's round():
Returns the rounded value of val to specified precision (number of digits after the decimal point)
There are optional flags to round 0.5
either up or down. But default is to round 0.5 >=
up.
I put it back to floor()
instead of round()
, and ran it again with fewer iterations. The results were much less stunning:
.... (truncated) ....
Calling color inverse test 991: e1254b != e1244a
Calling color inverse test 992: 179267 != 169266
Calling color inverse test 993: 7193b7 == 7193b7
Calling color inverse test 994: 91784c == 91784c
Calling color inverse test 995: 791e44 != 791d44
Calling color inverse test 996: 1fc79c == 1fc79c
Calling color inverse test 997: 17a527 != 17a526
Calling color inverse test 998: bfc86e != bfc86d
Calling color inverse test 999: c17ffd == c17ffd
Calling color inverse test 1000: da1e6b != da1d6b
======================================================
Completed 1000 tests. For 1000 tests, we passed 477 (47.7%)
I've got the CSS -> RGB -> HSV -> RGB -> CSS
working perfectly, but not light -> dark -> light
.
Is that expected?
Do you mean to alter a color to dark and then alter it back and get the same?
Yes.
Okay, yes. It's not as precise.
643658 -> 9b5488 -> 643658
Completed: 996 of 1000
3c96f1 -> 03090e -> 349bf1
Completed: 997 of 1000
f30c09 -> 0c0100 -> f31400
Completed: 998 of 1000
e11175 -> 1e0210 -> e10f78
Completed: 999 of 1000
fcf6d6 -> 030303 -> fcfcfc
Completed: 1000 of 1000
6be5cd -> 0c1a17 -> 6ae5cb
======================================================
Completed 1000 tests in 31.484 seconds.
For 1000 tests, we passed 243 (24.3%)
Here, passed
just means a perfect conversion.
Splendid. The Python version is in line with that…
I got the CSS -> RGB -> HSV -> RGB -> CSS
working 100% correctly, but it was my suspicion that invert(invert(CSS))
was inherently lossy.
Closing this issue. Feel free to reopen.
Just a bit more on the accuracy... I wrote up some functions to test the difference of colors between the recursive alter test, and it looks like there is an average loss of 1.5%
in the color integrity.
Looking at the data manually, it seems like no color bit is off by more than 5
(10
-> 15
, or c9
-> ce
).
My editor automatically highlights the CSS colors, so here's a crappy visual representation:
So, I decided to look further into the color conversion algorithms that I was using.
I went back to the source (equations) and rewrote them. They work much better now, especially because if you convert
hex->rgb->hsv->rgb->hex
, you'll get the same output as input. One major issue that I found was that PHP's%
plays only with integers, so I had to switch tofmod
. Regardless, I think everything is cleaner and nicer. We need to make sure that the logic in the other languages can spit out the same hex after those conversions.As a bonus, I wrote a little test script for PHP (to test the functions), you'll find it under
tests/generic/color_conversion_tests.php
. When you run it, you're asked for a hex color, and then it shows you the conversions from the hex to hsv and back again.