我们有一个想要显示的热图。构成显示值的数字是未知的(除了它们是正整数)。数字的范围也是未知的(再次,除了它们将是正整数)。范围可以介于0到200或578和1M之间。这取决于未知数据。
我们想要获取未知范围的正整数并将其转换为缩放(压缩)范围,以在热图中显示RGB值。我希望这是有道理的。 谢谢!
我想澄清一下min / max值需要“插入”论坛。
答案 0 :(得分:21)
您需要先找到这些值的范围才能获得最小值和最大值。 然后,您需要创建一个颜色比例,如此图像下方的条形图。您可以尝试使用不同的函数将整数映射到RGB。你需要3个函数R(X),G(X),B(X)。看下面的图像,中间有B(X)峰,末端有R(X)峰,绿色在其他地方。只要您确保从未获得某个X值的两个(RGB),那么您就可以获得转换。
alt text http://www.globalwarmingart.com/images/a/aa/Annual_Average_Temperature_Map.jpg
编辑: 想想你可以在YUV空间周围采样一些单位圆。 alt text http://www.biocrawler.com/w/images/e/ec/Yuv.png
甚至只需下载高分辨率彩条并对其进行采样即可。
编辑2:
我刚刚面对色条生成并记住了MATLAB / Octave色条代码。我绘制了他们的数据并获得了以下图像。
答案 1 :(得分:11)
您希望将数据值转换为光照频率:
可见光的频率从约350nm(紫色)到650nm(红色):
alt text http://www.gamonline.com/new/catalog/colortheory/images/spectrum.gif
以下函数将指定范围内的数字转换为可见光范围,然后获取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;
使用我写下的功能:
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;
还有function i found on the internets,可将波长转换为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};
样品使用:
数据设置范围为10..65,000,000。此特定数据点的值为638,328:
color = DataPointToColor(638328, 10, 65000000);
答案 2 :(得分:7)
colorbar的功能
// value between 0 and 1 (percent)
function color(value) {
var RGB = {R:0,G:0,B:0};
// y = mx + b
// m = 4
// x = value
// y = RGB._
if (0 <= value && value <= 1/8) {
RGB.R = 0;
RGB.G = 0;
RGB.B = 4*value + .5; // .5 - 1 // b = 1/2
} else if (1/8 < value && value <= 3/8) {
RGB.R = 0;
RGB.G = 4*value - .5; // 0 - 1 // b = - 1/2
RGB.B = 1; // small fix
} else if (3/8 < value && value <= 5/8) {
RGB.R = 4*value - 1.5; // 0 - 1 // b = - 3/2
RGB.G = 1;
RGB.B = -4*value + 2.5; // 1 - 0 // b = 5/2
} else if (5/8 < value && value <= 7/8) {
RGB.R = 1;
RGB.G = -4*value + 3.5; // 1 - 0 // b = 7/2
RGB.B = 0;
} else if (7/8 < value && value <= 1) {
RGB.R = -4*value + 4.5; // 1 - .5 // b = 9/2
RGB.G = 0;
RGB.B = 0;
} else { // should never happen - value > 1
RGB.R = .5;
RGB.G = 0;
RGB.B = 0;
}
// scale for hex conversion
RGB.R *= 15;
RGB.G *= 15;
RGB.B *= 15;
return Math.round(RGB.R).toString(16)+''+Math.round(RGB.G).toString(16)+''+Math.round(RGB.B).toString(16);
}
答案 3 :(得分:4)
离开Chris H提供的图片,您可以将rgb值建模为:
r = min(max(0, 1.5-abs(1-4*(val-0.5))),1);
g = min(max(0, 1.5-abs(1-4*(val-0.25))),1);
b = min(max(0, 1.5-abs(1-4*val)),1);
答案 4 :(得分:2)
在不知道值的范围的情况下,没有太多可以做出的有意义的函数将任意范围的正整数映射到热图类型的颜色范围。< / p>
我认为您必须至少运行一次数据以获得最小值/最大值或提前了解它们。一旦你有了,你可以适当地规范化并使用任意数量的配色方案。最简单的解决方案是指定类似“hue”的内容并从HSV转换为RGB。
答案 5 :(得分:2)
继续Ian Boyd的出色回答,我需要一组可区分的颜色来构建热图。诀窍是找到一种区分近色的方法,我找到了一个解决方案,转换为HSV并根据值改变V,稍微强调在颜色范围的中间,以显示黄色和橙色。
以下是代码:
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
以及由此产生的热图,显示了欧洲经济共同体国家的人均GDP:
答案 6 :(得分:2)
这个回答可能有点晚了。我正在显示一些环境数据,并且需要相对于数据集的最大值和最小值将结果条从绿色变为红色(或者将任何值作为最大值和最小值传递给函数。无论如何,下面的内容实现了我认为,可以很容易地将蓝色变为红色。
// scale colour temp relatively
function getColourTemp(maxVal, minVal, actual) {
var midVal = (maxVal - minVal)/2;
var intR;
var intG;
var intB = Math.round(0);
if (actual >= midVal){
intR = 255;
intG = Math.round(255 * ((maxVal - actual) / (maxVal - midVal)));
}
else{
intG = 255;
intR = Math.round(255 * ((actual - minVal) / (midVal - minVal)));
}
return to_rgb(intR, intG, intB);
}
答案 7 :(得分:0)
man,您可以使用YUV颜色空间,仅用于演示目的,将其转换为RGB。
答案 8 :(得分:0)
有点晚了,但我试图做同样的事情,发现我可以将HSV修改为RGB以获得类似的结果。它与波长方法类似,但通过首先转换为波长的需要。只需用您的值替换H(假设值介于0和1之间),并将S和V修复为1.我发现此处的HSVtoRGB示例非常有用:
http://www.cs.rit.edu/~ncs/color/t_convert.html
但是,我必须更改行
h /= 60;
i = floor ( h );
到
h *= 5;
i = (int) h;
获得贯穿整个频谱的输出。
答案 9 :(得分:-1)
简单算法
// given a max and min value
float red,green,blue;
float range=max-min;
float mid=(max+min)/2.0;
//foreach value
red = (value[ii]-mid)/range;
if (red>0.0) {
//above mid = red-green
blue=0.0;
green = 1.0-red;
} else {
// lower half green-blue
blue=-red;
green = 1.0-blue;
red=0.0;
}
}
更复杂:
如果您的范围是几百万,但大多数都在0左右,您想要缩放它,以便上例中的“红色”是距中点的距离的对数。如果值为+/-
// assume equally distributed around 0 so max is the largest (or most negative number)
float range = log(fabs(max));
float mid=0.0
// foreach value
if (value[ii] > 0.0 ) {
// above mid = red-green
red = log(value[ii])/range;
blue=0.0;
green = 1.0 - red;
} else {
// below mid = green-blue
blue=-log(value[ii])/range;
green = 1.0 - blue;
red = 0.0;
}
注意 - 我没有测试过这段代码,只是在思考!