Algorithm to convert any positive integer to an RGB value

后端 未结 10 1290
没有蜡笔的小新
没有蜡笔的小新 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:05

    Continuing from Ian Boyd's excellent answer, I needed a distinguishable set of colours to build a heatmap. The trick was to find a way to differentiate close colours and I found a solution by converting to HSV and varying the V according to the value, with a little emphasis in the middle of the colour range to bring out the yellows and oranges.

    Here's the code:

    Imports System.Drawing
    Imports RGBHSV
    
    Module HeatToColour_
    
        ' Thanks to Ian Boyd's excellent post here:
        ' http://stackoverflow.com/questions/2374959/algorithm-to-convert-any-positive-integer-to-an-rgb-value
    
        Private Const MinVisibleWaveLength As Double = 450.0
        Private Const MaxVisibleWaveLength As Double = 700.0
        Private Const Gamma As Double = 0.8
        Private Const IntensityMax As Integer = 255
    
        Function HeatToColour(ByVal value As Double, ByVal MinValue As Double, ByVal MaxValues As Double) As System.Drawing.Color
    
            Dim wavelength As Double
            Dim Red As Double
            Dim Green As Double
            Dim Blue As Double
            Dim Factor As Double
            Dim scaled As Double
    
            scaled = (value - MinValue) / (MaxValues - MinValue)
    
            wavelength = scaled * (MaxVisibleWaveLength - MinVisibleWaveLength) + MinVisibleWaveLength
    
            Select Case Math.Floor(wavelength)
    
                Case 380 To 439
                    Red = -(wavelength - 440) / (440 - 380)
                    Green = 0.0
                    Blue = 1.0
    
                Case 440 To 489
                    Red = 0.0
                    Green = (wavelength - 440) / (490 - 440)
                    Blue = 1.0
    
                Case 490 To 509
                    Red = 0.0
                    Green = 1.0
                    Blue = -(wavelength - 510) / (510 - 490)
    
                Case 510 To 579
                    Red = (wavelength - 510) / (580 - 510)
                    Green = 1.0
                    Blue = 0.0
    
                Case 580 To 644
                    Red = 1.0
                    Green = -(wavelength - 645) / (645 - 580)
                    Blue = 0.0
    
                Case 645 To 780
                    Red = 1.0
                    Green = 0.0
                    Blue = 0.0
    
                Case Else
                    Red = 0.0
                    Green = 0.0
                    Blue = 0.0
    
            End Select
    
            ' Let the intensity fall off near the vision limits
            Select Case Math.Floor(wavelength)
                Case 380 To 419
                    Factor = 0.3 + 0.7 * (wavelength - 380) / (420 - 380)
                Case 420 To 700
                    Factor = 1.0
                Case 701 To 780
                    Factor = 0.3 + 0.7 * (780 - wavelength) / (780 - 700)
                Case Else
                    Factor = 0.0
            End Select
    
            Dim R As Integer = Adjust(Red, Factor)
            Dim G As Integer = Adjust(Green, Factor)
            Dim B As Integer = Adjust(Blue, Factor)
    
            Dim result As Color = System.Drawing.Color.FromArgb(255, R, G, B)
            Dim resulthsv As New HSV
            resulthsv = ColorToHSV(result)
            resulthsv.Value = 0.7 + 0.1 * scaled + 0.2 * Math.Sin(scaled * Math.PI)
    
            result = HSVToColour(resulthsv)
    
            Return result
    
        End Function
        Private Function Adjust(ByVal Colour As Double, ByVal Factor As Double) As Integer
            If Colour = 0 Then
                Return 0
            Else
                Return Math.Round(IntensityMax * Math.Pow(Colour * Factor, Gamma))
            End If
        End Function
    
    End Module
    
    Imports System.Drawing
    Public Module RGBHSV
    
        Public Class HSV
            Sub New()
                Hue = 0
                Saturation = 0
                Value = 0
            End Sub
            Public Sub New(ByVal H As Double, ByVal S As Double, ByVal V As Double)
                Hue = H
                Saturation = S
                Value = V
            End Sub
            Public Hue As Double
            Public Saturation As Double
            Public Value As Double
        End Class
    
        Public Function ColorToHSV(ByVal color As Color) As HSV
            Dim max As Integer = Math.Max(color.R, Math.Max(color.G, color.B))
            Dim min As Integer = Math.Min(color.R, Math.Min(color.G, color.B))
            Dim result As New HSV
            With result
                .Hue = color.GetHue()
                .Saturation = If((max = 0), 0, 1.0 - (1.0 * min / max))
                .Value = max / 255.0
            End With
            Return result
        End Function
    
        Public Function HSVToColour(ByVal hsv As HSV) As Color
            Dim hi As Integer
            Dim f As Double
    
            With hsv
                hi = Convert.ToInt32(Math.Floor(.Hue / 60)) Mod 6
                f = .Hue / 60 - Math.Floor(.Hue / 60)
                .Value = .Value * 255
                Dim v As Integer = Convert.ToInt32(.Value)
                Dim p As Integer = Convert.ToInt32(.Value * (1 - .Saturation))
                Dim q As Integer = Convert.ToInt32(.Value * (1 - f * .Saturation))
                Dim t As Integer = Convert.ToInt32(.Value * (1 - (1 - f) * .Saturation))
    
                If hi = 0 Then
                    Return Color.FromArgb(255, v, t, p)
                ElseIf hi = 1 Then
                    Return Color.FromArgb(255, q, v, p)
                ElseIf hi = 2 Then
                    Return Color.FromArgb(255, p, v, t)
                ElseIf hi = 3 Then
                    Return Color.FromArgb(255, p, q, v)
                ElseIf hi = 4 Then
                    Return Color.FromArgb(255, t, p, v)
                Else
                    Return Color.FromArgb(255, v, p, q)
                End If
            End With
        End Function
    
    End Module
    

    and a resulting heatmap, showing GDP per capita for the countries in the EEC: GDP/Capita, EEC

提交回复
热议问题