What is the algorithm to create colors for a heatm

2019-01-31 08:00发布

问题:

Assuming values are normalized from 0 to 1, what is the algoritm to get a color to create a heatmap like this?

1 is red, .5 is green, 0 is dark blue.

Working in RMagick / ImageMagick.

回答1:

Here is a JavaScript code snippet to generate CSS hsl color code from [0, 1] value

function heatMapColorforValue(value){
  var h = (1.0 - value) * 240
  return "hsl(" + h + ", 100%, 50%)";
}

This algorithm is based on the 5 color heatmap,

In this algorithm, the colors corresponding with values are

0    : blue   (hsl(240, 100%, 50%))
0.25 : cyan   (hsl(180, 100%, 50%))
0.5  : green  (hsl(120, 100%, 50%))
0.75 : yellow (hsl(60, 100%, 50%))
1    : red    (hsl(0, 100%, 50%))

So simple!



回答2:

I found this surprisingly easy to do with HSL.

In Ruby:

def heatmap_color_for value # [0,1]
  h = (1 - value) * 100
  s = 100
  l = value * 50
  "hsl(#{h.round(2)}%,#{s.round(2)}%,#{l.round(2)}%)"
end

This method returns HSL values as a string between 0% and 100%. It can be used with RMagick or ImageMagick.

Reference: ImageMagick HSL documentation.



回答3:

Linear interpolation of the RGB components works quite well in practice, and the link Bruno shared mentions doing your interpolation in HSL which can help.

You can also intersperse your three basic colours with more nicely chosen intermediates. Check out http://colorbrewer2.org/ for some good colour progressions. Then break up your steps further:

0    red
0.25 yellow
0.5  green
0.75 cyan
1    blue


回答4:

A general approach is to interpolate colors. You decided that

0: 0 0 255 (or any blue)
0.5: 0 255 0 (or any green)
1: 255 0 0 (or any red)

You simply do a linear interpolation of the RGB. Between 2 reference values (eg t between 0 and 0.5), the interpolated color C is like

C = (1 - t) * c0 + t * c1

You must apply this formula on each color component RGB. Some other hints about color linear interpolation: How to interpolate a color sequence?

---- edit ----- I removed the header of my answer, as I realized I misunderstood the question (see comment). I leave a copy for consistent reading, and information, just in case.

A first possibility is to build a reference heatmap with any software that would: create a image 256X1pixel with pixel values from 0 to 255 and apply the desired heatmap with ImageMagick: then you can read the RGB back and build a map (value:RGB).



回答5:

I leave here a Swift 4 implementation based on this blog post for any amount of colors! Perfect explanation is there! Hope it helps and saves some time to someone!

import Foundation
import UIKit

struct ColorPoint {
    let color: UIColor
    let value: CGFloat
}

class HeatMapColor {
    var colorPoints: [ColorPoint]

    init(colorPoints: [ColorPoint]) {
        self.colorPoints = colorPoints
    }

    func colorAt(value: CGFloat) -> UIColor {
        if(colorPoints.isEmpty) { return UIColor.black }

        let colorsPointsToUse = colorPoints.sorted { (colorPointA, colorPointB) -> Bool in
            return colorPointA.value <= colorPointB.value
        }

        for (index, colorPoint) in colorsPointsToUse.enumerated() where value < colorPoint.value {
            let previousColorPoint = colorsPointsToUse[max(0, index - 1)]
            let valueDiff = previousColorPoint.value - colorPoint.value
            let fraction = valueDiff == 0 ? 0 : (value - colorPoint.value) / valueDiff

            guard
                let prevComp = previousColorPoint.color.cgColor.components,
                let currComp = colorPoint.color.cgColor.components else { continue }

            let red = (prevComp[0] - currComp[0]) * fraction + currComp[0]
            let green = (prevComp[1] - currComp[1]) * fraction + currComp[1]
            let blue = (prevComp[2] - currComp[2]) * fraction + currComp[2]

            return UIColor(red: red, green: green, blue: blue, alpha: 1.0)
        }

        return colorsPointsToUse.last!.color
    }
}


回答6:

Here's a simple 5 color heatmap in python (in pyqt, but easy to generalize)

def genColorMap(self):
    points = [(255,0,0), (255,255,0), (0,255,0), (0,255,255), (0,0,255)]
    cm = {}
    for i in range(0, 256):
        p0 = int(numpy.floor((i/256.0)/len(points)))
        p1 = int(numpy.ceil((i/256.0)/len(points)))
        rgb = map(lambda x: x[0]*max(0,(i-p0)) + x[1]*max(0,(i-p1)), zip(points[p0], points[p1]))
        cm[i] = QtGui.qRgb(rgb[0], rgb[1], rgb[2])
    return cm