algorithm - online - Algoritmo para convertir cualquier entero positivo a un valor RGB
rgb to hex google (10)
Tenemos un mapa de calor que queremos mostrar. Los números que conformarán los valores que se muestran son desconocidos (excepto que serán enteros positivos). El rango de números también es desconocido (nuevamente, excepto que serán enteros positivos). El rango podría estar entre 0 y 200 o 578 y 1M o lo que sea. Depende de los datos, lo cual es desconocido.
Queremos tomar un rango desconocido de enteros positivos y convertirlo en un rango escalado (comprimido) para mostrarlo con valores RGB en un mapa de calor. Espero que esto tenga sentido. ¡Gracias!
Quiero aclarar que los valores mínimos / máximos deben estar "conectados" en el foro.
Algoritmo simple
// 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;
}
}
Mas complejo:
Si su rango es de unos pocos millones, pero la mayoría está alrededor de 0, debe escalarlo, de modo que ''rojo'' en el ejemplo anterior sea el registro de la distancia desde el punto medio. El bacalao es un poco más complicado si los valores son +/-
// 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;
}
nota - ¡No he probado este código, solo estoy girando ideas!
Continuando con la excelente respuesta de Ian Boyd, necesitaba un conjunto de colores distinguibles para construir un mapa de calor. El truco consistía en encontrar una manera de diferenciar los colores cercanos y encontré una solución al convertir HSV y variando la V según el valor, con un poco de énfasis en el centro de la gama de colores para resaltar los amarillos y naranjas.
Aquí está el código:
Imports System.Drawing
Imports RGBHSV
Module HeatToColour_
'' Thanks to Ian Boyd''s excellent post here:
'' http://.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
y un mapa de calor resultante, que muestra el PIB per cápita de los países de la CEE:
Desea convertir sus valores de datos a una frecuencia de luz:
- longitud de onda inferior = colores más fríos = azulado
- mayor longitud de onda = colores más cálidos = más rojo
Las frecuencias de luz visible van desde aproximadamente 350nm (violeta) a 650nm (rojo):
texto alternativo http://www.gamonline.com/new/catalog/colortheory/images/spectrum.gif
La siguiente función convierte los números en su rango especificado al rango de luz visible, luego obtiene el 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;
Con la función que escribí de la parte superior de mi cabeza:
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;
Y una función que encontré en las redes internas , que convierte una longitud de onda en 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};
Uso de la muestra:
Datos establecidos en el rango de 10..65,000,000. Y este punto de datos en particular tiene un valor de 638,328:
color = DataPointToColor(638328, 10, 65000000);
Esta respuesta es probablemente un poco tarde para la fiesta. Estoy mostrando algunos datos ambientales, y necesito colorear las barras resultantes de verde a rojo en relación con el máximo y mínimo del conjunto de datos (o los valores que se hayan pasado como máximo y mínimo a la función. De todos modos, lo que sigue a continuación logra que Podría ser cambiado de azul a rojo con bastante facilidad, pensaría yo.
// 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);
}
Función para barra de colores
// 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);
}
Hombre, probablemente podrías usar el espacio de color YUV y solo para fines de demostración convertirlo a RGB.
Primero debes encontrar el rango de esos valores para obtener los valores mínimo y máximo. Entonces necesitas crear una escala de color como la barra debajo de esta imagen. Puede experimentar con diferentes funciones para asignar un número entero a un RGB. Necesita 3 funciones R (X), G (X), B (X). Al mirar la imagen debajo, se ve como picos B (X) en el medio, picos R (X) al final y verde en otra parte. Siempre que se asegure de que nunca obtenga dos (RGB) por algún valor de X, entonces obtendrá su conversión.
texto alt http://www.globalwarmingart.com/images/a/aa/Annual_Average_Temperature_Map.jpg
EDITAR: Piénsalo, puedes muestrear un círculo unitario alrededor del espacio YUV. texto alt http://www.biocrawler.com/w/images/e/ec/Yuv.png
O incluso simplemente descargue una barra de color de alta resolución y muestre eso.
EDIT 2: Me enfrenté a la generación de barras de colores y recordé el código de la barra de colores MATLAB / Octave. Traje sus datos y obtuve la siguiente imagen.
Saliendo de la imagen provista por Chris H, puede modelar los valores rgb como:
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);
Sin conocer el rango de valores, no hay mucho que pueda hacer para encontrar una función significativa que asigne un rango arbitrario de enteros positivos a un rango de colores de tipo mapa de calor.
Creo que tendrá que revisar sus datos al menos una vez para obtener el mínimo / máximo o conocerlos antes de tiempo. Una vez que tenga eso, puede normalizarse adecuadamente y usar cualquier número de combinaciones de colores. La solución más simple sería especificar algo como "tono" y convertir de HSV a RGB.
Un poco tarde, pero estaba tratando de hacer lo mismo y descubrí que puedo modificar HSV a RGB para obtener un resultado similar. Es similar al enfoque de la longitud de onda, pero al pasar la necesidad de convertir primero a la longitud de onda. Simplemente sustituya H con su valor (asumiendo un valor entre 0 y 1), y corrija S y V a 1. Encontré que el ejemplo de HSVtoRGB es muy útil:
http://www.cs.rit.edu/~ncs/color/t_convert.html
Sin embargo, tuve que cambiar las líneas.
h /= 60;
i = floor ( h );
a
h *= 5;
i = (int) h;
Para obtener una salida que atraviesa todo el espectro.
Recurso adicional: http://www.easyrgb.com/index.php?X=MATH&H=21#text21