FastLED 3.9.15
Loading...
Searching...
No Matches

◆ 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 550 of file hsv2rgb.cpp.

551{
552 uint8_t r = rgb.r;
553 uint8_t g = rgb.g;
554 uint8_t b = rgb.b;
555 uint8_t h, s, v;
556
557 // find desaturation
558 uint8_t desat = 255;
559 if( r < desat) desat = r;
560 if( g < desat) desat = g;
561 if( b < desat) desat = b;
562
563 // remove saturation from all channels
564 r -= desat;
565 g -= desat;
566 b -= desat;
567
568 //Serial.print("desat="); Serial.print(desat); Serial.println("");
569
570 //uint8_t orig_desat = sqrt16( desat * 256);
571 //Serial.print("orig_desat="); Serial.print(orig_desat); Serial.println("");
572
573 // saturation is opposite of desaturation
574 s = 255 - desat;
575 //Serial.print("s.1="); Serial.print(s); Serial.println("");
576
577 if( s != 255 ) {
578 // undo 'dimming' of saturation
579 s = 255 - sqrt16( (255-s) * 256);
580 }
581 // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
582 // if( s != 255 ) s = (255 - (256.0 * sqrt( (float)(255-s) / 256.0)));
583 //Serial.print("s.2="); Serial.print(s); Serial.println("");
584
585
586 // at least one channel is now zero
587 // if all three channels are zero, we had a
588 // shade of gray.
589 if( (r + g + b) == 0) {
590 // we pick hue zero for no special reason
591 return CHSV( 0, 0, 255 - s);
592 }
593
594 // scale all channels up to compensate for desaturation
595 if( s < 255) {
596 if( s == 0) s = 1;
597 uint32_t scaleup = 65535 / (s);
598 r = ((uint32_t)(r) * scaleup) / 256;
599 g = ((uint32_t)(g) * scaleup) / 256;
600 b = ((uint32_t)(b) * scaleup) / 256;
601 }
602 //Serial.print("r.2="); Serial.print(r); Serial.println("");
603 //Serial.print("g.2="); Serial.print(g); Serial.println("");
604 //Serial.print("b.2="); Serial.print(b); Serial.println("");
605
606 uint16_t total = r + g + b;
607
608 //Serial.print("total="); Serial.print(total); Serial.println("");
609
610 // scale all channels up to compensate for low values
611 if( total < 255) {
612 if( total == 0) total = 1;
613 uint32_t scaleup = 65535 / (total);
614 r = ((uint32_t)(r) * scaleup) / 256;
615 g = ((uint32_t)(g) * scaleup) / 256;
616 b = ((uint32_t)(b) * scaleup) / 256;
617 }
618 //Serial.print("r.3="); Serial.print(r); Serial.println("");
619 //Serial.print("g.3="); Serial.print(g); Serial.println("");
620 //Serial.print("b.3="); Serial.print(b); Serial.println("");
621
622 if( total > 255 ) {
623 v = 255;
624 } else {
625 v = qadd8(desat,total);
626 // undo 'dimming' of brightness
627 if( v != 255) v = sqrt16( v * 256);
628 // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
629 // if( v != 255) v = (256.0 * sqrt( (float)(v) / 256.0));
630
631 }
632
633 //Serial.print("v="); Serial.print(v); Serial.println("");
634
635
636#if 0
637
638 //#else
639 if( v != 255) {
640 // this part could probably use refinement/rethinking,
641 // (but it doesn't overflow & wrap anymore)
642 uint16_t s16;
643 s16 = (s * 256);
644 s16 /= v;
645 //Serial.print("s16="); Serial.print(s16); Serial.println("");
646 if( s16 < 256) {
647 s = s16;
648 } else {
649 s = 255; // clamp to prevent overflow
650 }
651 }
652#endif
653
654 //Serial.print("s.3="); Serial.print(s); Serial.println("");
655
656
657 // since this wasn't a pure shade of gray,
658 // the interesting question is what hue is it
659
660
661
662 // start with which channel is highest
663 // (ties don't matter)
664 uint8_t highest = r;
665 if( g > highest) highest = g;
666 if( b > highest) highest = b;
667
668 if( highest == r ) {
669 // Red is highest.
670 // Hue could be Purple/Pink-Red,Red-Orange,Orange-Yellow
671 if( g == 0 ) {
672 // if green is zero, we're in Purple/Pink-Red
673 h = (HUE_PURPLE + HUE_PINK) / 2;
674 h += scale8( qsub8(r, 128), FIXFRAC8(48,128));
675 } else if ( (r - g) > g) {
676 // if R-G > G then we're in Red-Orange
677 h = HUE_RED;
678 h += scale8( g, FIXFRAC8(32,85));
679 } else {
680 // R-G < G, we're in Orange-Yellow
681 h = HUE_ORANGE;
682 h += scale8( qsub8((g - 85) + (171 - r), 4), FIXFRAC8(32,85)); //221
683 }
684
685 } else if ( highest == g) {
686 // Green is highest
687 // Hue could be Yellow-Green, Green-Aqua
688 if( b == 0) {
689 // if Blue is zero, we're in Yellow-Green
690 // G = 171..255
691 // R = 171.. 0
692 h = HUE_YELLOW;
693 uint8_t radj = scale8( qsub8(171,r), 47); //171..0 -> 0..171 -> 0..31
694 uint8_t gadj = scale8( qsub8(g,171), 96); //171..255 -> 0..84 -> 0..31;
695 uint8_t rgadj = radj + gadj;
696 uint8_t hueadv = rgadj / 2;
697 h += hueadv;
698 //h += scale8( qadd8( 4, qadd8((g - 128), (128 - r))),
699 // FIXFRAC8(32,255)); //
700 } else {
701 // if Blue is nonzero we're in Green-Aqua
702 if( (g-b) > b) {
703 h = HUE_GREEN;
704 h += scale8( b, FIXFRAC8(32,85));
705 } else {
706 h = HUE_AQUA;
707 h += scale8( qsub8(b, 85), FIXFRAC8(8,42));
708 }
709 }
710
711 } else /* highest == b */ {
712 // Blue is highest
713 // Hue could be Aqua/Blue-Blue, Blue-Purple, Purple-Pink
714 if( r == 0) {
715 // if red is zero, we're in Aqua/Blue-Blue
716 h = HUE_AQUA + ((HUE_BLUE - HUE_AQUA) / 4);
717 h += scale8( qsub8(b, 128), FIXFRAC8(24,128));
718 } else if ( (b-r) > r) {
719 // B-R > R, we're in Blue-Purple
720 h = HUE_BLUE;
721 h += scale8( r, FIXFRAC8(32,85));
722 } else {
723 // B-R < R, we're in Purple-Pink
724 h = HUE_PURPLE;
725 h += scale8( qsub8(r, 85), FIXFRAC8(32,85));
726 }
727 }
728
729 h += 1;
730 return CHSV( h, s, v);
731}
@ HUE_BLUE
Blue (225°)
Definition hsv.h:102
@ HUE_RED
Red (0°)
Definition hsv.h:97
@ HUE_AQUA
Aqua (180°)
Definition hsv.h:101
@ HUE_PINK
Pink (315°)
Definition hsv.h:104
@ HUE_GREEN
Green (135°)
Definition hsv.h:100
@ HUE_YELLOW
Yellow (90°)
Definition hsv.h:99
@ HUE_ORANGE
Orange (45°)
Definition hsv.h:98
@ HUE_PURPLE
Purple (270°)
Definition hsv.h:103
LIB8STATIC_ALWAYS_INLINE uint8_t qadd8(uint8_t i, uint8_t j)
Add one byte to another, saturating at 0xFF.
Definition math8.h:40
LIB8STATIC uint8_t sqrt16(uint16_t x)
Square root for 16-bit integers.
Definition math8.h:540
LIB8STATIC_ALWAYS_INLINE uint8_t qsub8(uint8_t i, uint8_t j)
Subtract one byte from another, saturating at 0x00.
Definition math8.h:112
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:44
#define FIXFRAC8(N, D)
Convert a fractional input into a constant.
Definition hsv2rgb.cpp:545
Representation of an HSV pixel (hue, saturation, value (aka brightness)).
Definition hsv.h:15

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: