web-dev-qa-db-ja.com

任意の正の整数をRGB値に変換するアルゴリズム

表示したいヒートマップがあります。表示される値を構成する数値は不明です(正の整数になることを除いて)。数の範囲も不明です(これも、正のインターガーになることを除いて)。範囲は、0〜200、578〜1Mなどです。それは不明なデータに依存します。

未知の範囲の正の整数を取得し、それをスケーリングされた(圧縮された)範囲に変換して、ヒートマップにRGB値で表示したいと考えています。これが理にかなっていることを願っています。ありがとう!

最小/最大値をフォーラムに「プラグイン」する必要があることを明確にしたい。

21
Richard

最小値と最大値を取得するには、最初にこれらの値の範囲を見つける必要があります。次に、この画像の下にあるバーのようなカラースケールを作成する必要があります。さまざまな関数を試して、整数をRGBにマッピングできます。 3つの関数R(X)、G(X)、B(X)が必要です。下の画像を見ると、B(X)中央にピークがあり、R(X)最後にピークがあり、緑はどこかにあります。 Xの値に対して2つ(RGB)を取得しないことを確認する限り、変換を取得できます。

alt text
(出典: globalwarmingart.com

編集:考えてみると、YUVスペースの周りの単位円をサンプリングできます。 代替テキストhttp://www.biocrawler.com/w/images/e/ec/Yuv.png

または、高解像度のカラーバーをダウンロードしてサンプルすることもできます。

編集2:カラーバーの生成に直面し、MATLAB/Octaveカラーバーコードを思い出しました。私は彼らのデータをプロットし、次の画像を取得しました。 alt text

21
Chris H

データ値を光の周波数に変換したい:

  • より低い波長=より涼しい色=青みがかった
  • より高い波長=より暖かい色=より赤い

可視光の周波数は約350nm(紫)から650nm(赤)になります。

alt text
(出典: 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);
11
Ian Boyd

カラーバーの機能

// 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);
}
7
will Farrell

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

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

結果として得られるヒートマップは、EEC諸国の1人当たりGDPを示しています。 GDP/Capita, EEC

2
smirkingman

値の範囲がわからなければ、任意の範囲の正の整数をヒートにマッピングする意味のある関数を思い付くためにできることはあまりありません。 -マップタイプの色の範囲。

最小/最大を取得するか、事前にデータを知るには、少なくとも1回はデータを実行する必要があると思います。それができたら、適切に正規化し、任意の数の配色を使用できます。最も簡単な解決策は、「hue」のようなものを指定し、 [〜#〜] hsv [〜#〜] からRGBに変換することです。

2
job

この答えはおそらくパーティーに少し遅れています。いくつかの環境データを表示しているので、データセットの最大値と最小値(または関数に最大値と最小値として渡される値)を基準にして、結果のバーを緑から赤に色付けする必要があります。とにかく、以下はそれを実現します。青から赤にかなり簡単に変更できると思います。

// 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);
}
2
Mark Lazz

男、あなたはおそらくYUV色空間を使用することができ、デモンストレーション目的でのみそれをRGBに変換します。

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;

スペクトル全体を通過する出力を取得します。

追加リソース: http://www.easyrgb.com/index.php?X=MATH&H=21#text21

0
omikun