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 551 of file hsv2rgb.cpp.hpp.

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

References FIXFRAC8, HUE_AQUA, HUE_BLUE, HUE_GREEN, HUE_ORANGE, HUE_PINK, HUE_PURPLE, HUE_RED, and HUE_YELLOW.