Algorithm to convert any positive integer to an RGB value

后端 未结 10 1292
没有蜡笔的小新
没有蜡笔的小新 2020-12-04 13:49

We have a heatmap we want to display. The numbers that will make up the values being displayed are unknown (except that they will be positive integers). The range of numbers

相关标签:
10条回答
  • 2020-12-04 14:23

    man, you could probably use YUV color space and only for demonstration purposes convert it to RGB.

    0 讨论(0)
  • 2020-12-04 14:24

    You want to convert your data values to a frequency of light:

    • lower wavelength = cooler colors = blueish
    • higher wavelength = warmer colors = redder

    The frequencies of visible light go from about 350nm (violet) to 650nm (red):


    (source: gamonline.com)

    The following function converts numbers in your specified range to the the range of visible light, then gets the rgb:

    function DataPointToColor(Value, MinValue, MaxValue: Real): TColor;
    var
       r, g, b: Byte;
       WaveLength: Real;
    begin
       WaveLength := GetWaveLengthFromDataPoint(Value, MinValue, MaxValue);
       WavelengthToRGB(Wavelength, r, g, b);
       Result := RGB(r, g, b);
    end;
    

    With the function i wrote off the top of my head:

    function GetWaveLengthFromDataPoint(Value: Real; MinValues, MaxValues: Real): Real;
    const
       MinVisibleWaveLength = 350.0;
       MaxVisibleWaveLength = 650.0;
    begin
       //Convert data value in the range of MinValues..MaxValues to the 
       //range 350..650
    
       Result := (Value - MinValue) / (MaxValues-MinValues) *
             (MaxVisibleWavelength - MinVisibleWavelength) +
             MinVisibleWaveLength;
    end;
    

    And a function i found on the internets, that converts a wavelength into RGB:

    PROCEDURE WavelengthToRGB(CONST Wavelength:  Nanometers;
                              VAR R,G,B:  BYTE);
      CONST
        Gamma        =   0.80;
        IntensityMax = 255;
      VAR
        Blue   :  DOUBLE;
        factor :  DOUBLE;
        Green  :  DOUBLE;
        Red    :  DOUBLE;
      FUNCTION Adjust(CONST Color, Factor:  DOUBLE):  INTEGER;
      BEGIN
        IF   Color = 0.0
        THEN RESULT := 0     // Don't want 0^x = 1 for x <> 0
        ELSE RESULT := ROUND(IntensityMax * Power(Color * Factor, Gamma))
      END {Adjust};
    BEGIN
      CASE TRUNC(Wavelength) OF
        380..439:
          BEGIN
            Red   := -(Wavelength - 440) / (440 - 380);
            Green := 0.0;
            Blue  := 1.0
          END;
        440..489:
          BEGIN
            Red   := 0.0;
            Green := (Wavelength - 440) / (490 - 440);
            Blue  := 1.0
          END;
        490..509:
          BEGIN
            Red   := 0.0;
            Green := 1.0;
            Blue  := -(Wavelength - 510) / (510 - 490)
          END;
        510..579:
          BEGIN
            Red   := (Wavelength - 510) / (580 - 510);
            Green := 1.0;
            Blue  := 0.0
          END;
        580..644:
          BEGIN
            Red   := 1.0;
            Green := -(Wavelength - 645) / (645 - 580);
            Blue  := 0.0
          END;
        645..780:
          BEGIN
            Red   := 1.0;
            Green := 0.0;
            Blue  := 0.0
          END;
        ELSE
          Red   := 0.0;
          Green := 0.0;
          Blue  := 0.0
      END;
      // Let the intensity fall off near the vision limits
      CASE TRUNC(Wavelength) OF
        380..419:  factor := 0.3 + 0.7*(Wavelength - 380) / (420 - 380);
        420..700:  factor := 1.0;
        701..780:  factor := 0.3 + 0.7*(780 - Wavelength) / (780 - 700)
        ELSE       factor := 0.0
      END;
      R := Adjust(Red,   Factor);
      G := Adjust(Green, Factor);
      B := Adjust(Blue,  Factor)
    END {WavelengthToRGB}; 
    

    Sample use:

    Data set in the range of 10..65,000,000. And this particular data point has a value of 638,328:

    color = DataPointToColor(638328, 10, 65000000);
    
    0 讨论(0)
  • 2020-12-04 14:24

    Without knowing the range of values, there isn't much you can do to come up with a meaningful function mapping an arbitrary range of positive integers to a heat-map type range of colors.

    I think you're going to have to run through your data at least once to get the min/max or know them ahead of time. Once you have that you can normalize appropriately and use any number of color schemes. The simplest solution would be to specify something like "hue" and convert from HSV to RGB.

    0 讨论(0)
  • 2020-12-04 14:28

    A little late, but I was trying to do the same and found I can modify HSV to RGB to get a similar result. It's similar to the wavelength approach but by passes the need to convert to wavelength first. Just substitute H with your value (assuming a value between 0 and 1), and fix S and V to 1. I found the HSVtoRGB example here to be very helpful:

    http://www.cs.rit.edu/~ncs/color/t_convert.html

    However, I had to change the lines

    h /= 60;
    i = floor ( h );
    

    to

    h *= 5;
    i = (int) h;
    

    to get an output that goes through the whole spectrum.

    Additional resource: http://www.easyrgb.com/index.php?X=MATH&H=21#text21

    0 讨论(0)
提交回复
热议问题