Алгоритм для преобразования любого положительного целого числа в значение RGB
У нас есть карта нагрева, которую мы хотим отобразить. Номера, которые будут отображать отображаемые значения, неизвестны (за исключением того, что они будут целыми положительными). Диапазон чисел также неизвестен (опять же, за исключением того, что они будут позитивными целыми). Диапазон может составлять от 0 до 200 или 578 и 1 М или что-то еще. Это зависит от данных, которые неизвестны.
Мы хотим взять неизвестный диапазон положительных целых чисел и превратить его в масштабированный (сжатый) диапазон, который будет отображаться с значениями RGB в тепловой карте. Я надеюсь в этом есть смысл.
Спасибо!
Я хочу уточнить, что значения min/max должны быть "подключены" к forumla.
Ответы
Ответ 1
Сначала нужно найти диапазон этих значений, чтобы получить минимальное и максимальное значения. Затем вам нужно создать цветовую шкалу, как в строке ниже этого изображения. Вы можете поэкспериментировать с различными функциями, чтобы отобразить целое число в RGB. Вам нужно 3 функции R (X), G (X), B (X). Глядя на изображение ниже, оно выглядит как пики B (X) в середине, пики R (X) в конце, а зеленый - где-то еще. Если вы уверены, что никогда не получите два (RGB) для некоторого значения X, то вы получите свое преобразование.
(источник: globalwarmingart.com)
РЕДАКТИРОВАТЬ: Давай думать об этом, вы можете попробовать некоторые единицы круга вокруг пространства YUV. альтернативный текст http://www.biocrawler.com/w/images/e/ec/Yuv.png
Или даже просто скачайте цветную полосу высокого разрешения и попробуйте ее.
РЕДАКТИРОВАТЬ 2: Я только что столкнулся с генерацией цветовых полос и вспомнил код цветовой полосы MATLAB/Octave. Я нанес их на карту и получил следующее изображение. ![alt text]()
Ответ 2
Вы хотите преобразовать ваши значения данных в частоту света:
- меньшая длина волны = холодные цвета = голубоватый
- большая длина волны = теплые цвета = красный
Частоты видимого света варьируются от 350 нм (фиолетовый) до 650 нм (красный):
(источник: gamonline.com)
Следующая функция преобразует числа в указанном вами диапазоне в диапазон видимого света, а затем получает 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;
И функцию, которую я нашел в интернете, которая преобразует длину волны в 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);
Ответ 3
Функция для 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);
}
Ответ 4
Сместив изображение, предоставленное Крисом Х, вы можете моделировать значения 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);
Ответ 5
Не зная диапазон значений, вы не можете сделать, чтобы придумать значимую функцию, отображающую произвольный диапазон положительных целых чисел в диапазон цветов цвета тепловой карты.
Я думаю, вам придется пропустить ваши данные хотя бы один раз, чтобы получить min/max или узнать их раньше времени. После этого вы сможете нормально нормализовать и использовать любое количество цветовых схем. Простейшим решением было бы указать что-то вроде "hue" и преобразовать из HSV в RGB.
Ответ 6
Продолжая с Ян Бойда отличный ответ, мне понадобился различный набор цветов для создания тепловой карты. Хитрость заключалась в том, чтобы найти способ отличить близкие цвета, и я нашел решение, преобразовывая в HSV и меняя V в соответствии со значением, с небольшим акцентом в середине цветового диапазона, чтобы выявить желтые и апельсины.
Здесь код:
Imports System.Drawing
Imports RGBHSV
Module HeatToColour_
' Thanks to Ian Boyd 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/Capita, EEC]()
Ответ 7
Этот ответ, вероятно, немного опоздал на вечеринку. Я показываю некоторые данные об окружающей среде и вам нужно покрасить результирующие столбцы от зеленого до красного относительно максимального и минимального набора данных (или какие когда-либо значения передаются как max и min для функции. В любом случае ниже выполняется это Я бы подумал, что можно изменить для синего и красного достаточно легко.
// 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);
}
Ответ 8
человек, вы, вероятно, можете использовать цветовое пространство YUV и только для демонстрационных целей преобразовать его в RGB.
Ответ 9
Немного поздно, но я пытался сделать то же самое и обнаружил, что могу изменить HSV на RGB, чтобы получить аналогичный результат. Он похож на подход с длиной волны, но пропускает необходимость сначала преобразовать в длину волны. Просто замените H своим значением (при значении от 0 до 1) и исправьте S и V до 1. Я нашел пример HSVtoRGB здесь очень полезным:
http://www.cs.rit.edu/~ncs/color/t_convert.html
Однако мне пришлось изменить строки
h /= 60;
i = floor ( h );
to
h *= 5;
i = (int) h;
чтобы получить выход, проходящий через весь спектр.
Дополнительный ресурс:
http://www.easyrgb.com/index.php?X=MATH&H=21#text21
Ответ 10
Простой алгоритм
// 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, вы хотите масштабировать его так, чтобы "красный" в приведенном выше примере был журналом расстояния от середины. Треска eis немного сложнее, если значения +/-
// 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;
}
note - Я не тестировал этот код, просто вращаю идеи!