FastLED 3.9.15
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages

◆ rgb2hsv_approximate()

CHSV rgb2hsv_approximate ( const CRGB & rgb)

Recover approximate HSV values from RGB.

These values are approximate, not exact. Why is this "only" an approximation? Because not all RGB colors have HSV equivalents! For example, there is no HSV value that will ever convert to RGB(255,255,0) using the code provided in this library. So if you try to convert RGB(255,255,0) "back" to HSV, you'll necessarily get only an approximation. Emphasis has been placed on getting the "hue" as close as usefully possible, but even that's a bit of a challenge. The 8-bit HSV and 8-bit RGB color spaces are not a "bijection".

Nevertheless, this function does a pretty good job, particularly at recovering the 'hue' from fully saturated RGB colors that originally came from HSV rainbow colors. So if you start with CHSV(hue_in,255,255), and convert that to RGB, and then convert it back to HSV using this function, the resulting output hue will either exactly the same, or very close (+/-1). The more desaturated the original RGB color is, the rougher the approximation, and the less accurate the results.

Note
This function is a long-term work in progress; expect results to change slightly over time as this function is refined and improved.
Note
This function is most accurate when the input is an RGB color that came from a fully-saturated HSV color to start with. E.g. CHSV( hue, 255, 255) -> CRGB -> CHSV will give best results.
Note
This function is not nearly as fast as HSV-to-RGB. It is provided for those situations when the need for this function cannot be avoided, or when extremely high performance is not needed.
See also
https://en.wikipedia.org/wiki/Bijection
Parameters
rgban RGB value to convert
Returns
the approximate HSV equivalent of the RGB value

Definition at line 504 of file hsv2rgb.cpp.

505{
506 uint8_t r = rgb.r;
507 uint8_t g = rgb.g;
508 uint8_t b = rgb.b;
509 uint8_t h, s, v;
510
511 // find desaturation
512 uint8_t desat = 255;
513 if( r < desat) desat = r;
514 if( g < desat) desat = g;
515 if( b < desat) desat = b;
516
517 // remove saturation from all channels
518 r -= desat;
519 g -= desat;
520 b -= desat;
521
522 //Serial.print("desat="); Serial.print(desat); Serial.println("");
523
524 //uint8_t orig_desat = sqrt16( desat * 256);
525 //Serial.print("orig_desat="); Serial.print(orig_desat); Serial.println("");
526
527 // saturation is opposite of desaturation
528 s = 255 - desat;
529 //Serial.print("s.1="); Serial.print(s); Serial.println("");
530
531 if( s != 255 ) {
532 // undo 'dimming' of saturation
533 s = 255 - sqrt16( (255-s) * 256);
534 }
535 // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
536 // if( s != 255 ) s = (255 - (256.0 * sqrt( (float)(255-s) / 256.0)));
537 //Serial.print("s.2="); Serial.print(s); Serial.println("");
538
539
540 // at least one channel is now zero
541 // if all three channels are zero, we had a
542 // shade of gray.
543 if( (r + g + b) == 0) {
544 // we pick hue zero for no special reason
545 return CHSV( 0, 0, 255 - s);
546 }
547
548 // scale all channels up to compensate for desaturation
549 if( s < 255) {
550 if( s == 0) s = 1;
551 uint32_t scaleup = 65535 / (s);
552 r = ((uint32_t)(r) * scaleup) / 256;
553 g = ((uint32_t)(g) * scaleup) / 256;
554 b = ((uint32_t)(b) * scaleup) / 256;
555 }
556 //Serial.print("r.2="); Serial.print(r); Serial.println("");
557 //Serial.print("g.2="); Serial.print(g); Serial.println("");
558 //Serial.print("b.2="); Serial.print(b); Serial.println("");
559
560 uint16_t total = r + g + b;
561
562 //Serial.print("total="); Serial.print(total); Serial.println("");
563
564 // scale all channels up to compensate for low values
565 if( total < 255) {
566 if( total == 0) total = 1;
567 uint32_t scaleup = 65535 / (total);
568 r = ((uint32_t)(r) * scaleup) / 256;
569 g = ((uint32_t)(g) * scaleup) / 256;
570 b = ((uint32_t)(b) * scaleup) / 256;
571 }
572 //Serial.print("r.3="); Serial.print(r); Serial.println("");
573 //Serial.print("g.3="); Serial.print(g); Serial.println("");
574 //Serial.print("b.3="); Serial.print(b); Serial.println("");
575
576 if( total > 255 ) {
577 v = 255;
578 } else {
579 v = qadd8(desat,total);
580 // undo 'dimming' of brightness
581 if( v != 255) v = sqrt16( v * 256);
582 // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
583 // if( v != 255) v = (256.0 * sqrt( (float)(v) / 256.0));
584
585 }
586
587 //Serial.print("v="); Serial.print(v); Serial.println("");
588
589
590#if 0
591
592 //#else
593 if( v != 255) {
594 // this part could probably use refinement/rethinking,
595 // (but it doesn't overflow & wrap anymore)
596 uint16_t s16;
597 s16 = (s * 256);
598 s16 /= v;
599 //Serial.print("s16="); Serial.print(s16); Serial.println("");
600 if( s16 < 256) {
601 s = s16;
602 } else {
603 s = 255; // clamp to prevent overflow
604 }
605 }
606#endif
607
608 //Serial.print("s.3="); Serial.print(s); Serial.println("");
609
610
611 // since this wasn't a pure shade of gray,
612 // the interesting question is what hue is it
613
614
615
616 // start with which channel is highest
617 // (ties don't matter)
618 uint8_t highest = r;
619 if( g > highest) highest = g;
620 if( b > highest) highest = b;
621
622 if( highest == r ) {
623 // Red is highest.
624 // Hue could be Purple/Pink-Red,Red-Orange,Orange-Yellow
625 if( g == 0 ) {
626 // if green is zero, we're in Purple/Pink-Red
627 h = (HUE_PURPLE + HUE_PINK) / 2;
628 h += scale8( qsub8(r, 128), FIXFRAC8(48,128));
629 } else if ( (r - g) > g) {
630 // if R-G > G then we're in Red-Orange
631 h = HUE_RED;
632 h += scale8( g, FIXFRAC8(32,85));
633 } else {
634 // R-G < G, we're in Orange-Yellow
635 h = HUE_ORANGE;
636 h += scale8( qsub8((g - 85) + (171 - r), 4), FIXFRAC8(32,85)); //221
637 }
638
639 } else if ( highest == g) {
640 // Green is highest
641 // Hue could be Yellow-Green, Green-Aqua
642 if( b == 0) {
643 // if Blue is zero, we're in Yellow-Green
644 // G = 171..255
645 // R = 171.. 0
646 h = HUE_YELLOW;
647 uint8_t radj = scale8( qsub8(171,r), 47); //171..0 -> 0..171 -> 0..31
648 uint8_t gadj = scale8( qsub8(g,171), 96); //171..255 -> 0..84 -> 0..31;
649 uint8_t rgadj = radj + gadj;
650 uint8_t hueadv = rgadj / 2;
651 h += hueadv;
652 //h += scale8( qadd8( 4, qadd8((g - 128), (128 - r))),
653 // FIXFRAC8(32,255)); //
654 } else {
655 // if Blue is nonzero we're in Green-Aqua
656 if( (g-b) > b) {
657 h = HUE_GREEN;
658 h += scale8( b, FIXFRAC8(32,85));
659 } else {
660 h = HUE_AQUA;
661 h += scale8( qsub8(b, 85), FIXFRAC8(8,42));
662 }
663 }
664
665 } else /* highest == b */ {
666 // Blue is highest
667 // Hue could be Aqua/Blue-Blue, Blue-Purple, Purple-Pink
668 if( r == 0) {
669 // if red is zero, we're in Aqua/Blue-Blue
670 h = HUE_AQUA + ((HUE_BLUE - HUE_AQUA) / 4);
671 h += scale8( qsub8(b, 128), FIXFRAC8(24,128));
672 } else if ( (b-r) > r) {
673 // B-R > R, we're in Blue-Purple
674 h = HUE_BLUE;
675 h += scale8( r, FIXFRAC8(32,85));
676 } else {
677 // B-R < R, we're in Purple-Pink
678 h = HUE_PURPLE;
679 h += scale8( qsub8(r, 85), FIXFRAC8(32,85));
680 }
681 }
682
683 h += 1;
684 return CHSV( h, s, v);
685}
LIB8STATIC_ALWAYS_INLINE uint8_t qadd8(uint8_t i, uint8_t j)
Add one byte to another, saturating at 0xFF.
Definition math8.h:31
LIB8STATIC uint8_t sqrt16(uint16_t x)
Square root for 16-bit integers.
Definition math8.h:524
LIB8STATIC_ALWAYS_INLINE uint8_t qsub8(uint8_t i, uint8_t j)
Subtract one byte from another, saturating at 0x00.
Definition math8.h:103
@ HUE_ORANGE
Orange (45°)
Definition chsv.h:99
@ HUE_BLUE
Blue (225°)
Definition chsv.h:103
@ HUE_PINK
Pink (315°)
Definition chsv.h:105
@ HUE_YELLOW
Yellow (90°)
Definition chsv.h:100
@ HUE_PURPLE
Purple (270°)
Definition chsv.h:104
@ HUE_AQUA
Aqua (180°)
Definition chsv.h:102
@ HUE_GREEN
Green (135°)
Definition chsv.h:101
@ HUE_RED
Red (0°)
Definition chsv.h:98
LIB8STATIC_ALWAYS_INLINE uint8_t scale8(uint8_t i, fract8 scale)
Scale one byte by a second one, which is treated as the numerator of a fraction whose denominator is ...
Definition scale8.h:34
#define FIXFRAC8(N, D)
Convert a fractional input into a constant.
Definition hsv2rgb.cpp:499
Representation of an HSV pixel (hue, saturation, value (aka brightness)).
Definition chsv.h:16

References FIXFRAC8, HUE_AQUA, HUE_BLUE, HUE_GREEN, HUE_ORANGE, HUE_PINK, HUE_PURPLE, HUE_RED, HUE_YELLOW, qadd8(), qsub8(), scale8(), and sqrt16().

+ Here is the call graph for this function: