5

RGB と HSB の色値を切り替えるアルゴリズムの記事を読みました。

Type RGBColor
     Red As Byte
     Green As Byte
     Blue As Byte
End Type

Type HSBColor
     Hue As Double
     Saturation As Double
     Brightness As Double
End Type

Function RGBToHSB(rgb As RGBColor) As HSBColor
     Dim minRGB, maxRGB, Delta As Double
     Dim h, s, b As Double
     h = 0
     minRGB = Min(Min(rgb.Red, rgb.Green), rgb.Blue)
     maxRGB = Max(Max(rgb.Red, rgb.Green), rgb.Blue)
     Delta = (maxRGB - minRGB)
     b = maxRGB
     If (maxRGB <> 0) Then
          s = 255 * Delta / maxRGB
     Else
          s = 0
     End If
     If (s <> 0) Then
          If rgb.Red = maxRGB Then
               h = (CDbl(rgb.Green) - CDbl(rgb.Blue)) / Delta
          Else
               If rgb.Green = maxRGB Then
                    h = 2 + (CDbl(rgb.Blue) - CDbl(rgb.Red)) / Delta
               Else
                    If rgb.Blue = maxRGB Then
                         h = 4 + (CDbl(rgb.Red) - CDbl(rgb.Green)) / Delta
                    End If
               End If
          End If
     Else
          h = -1
     End If
     h = h * 60
     If h < 0 Then h = h + 360
     RGBToHSB.Hue = h
     RGBToHSB.Saturation = s * 100 / 255
     RGBToHSB.Brightness = b * 100 / 255
End Function

Function HSBToRGB(hsb As HSBColor) As RGBColor
     Dim maxRGB, Delta As Double
     Dim h, s, b As Double
     h = hsb.Hue / 60
     s = hsb.Saturation * 255 / 100
     b = hsb.Brightness * 255 / 100
     maxRGB = b
     If s = 0 Then
          HSBToRGB.Red = 0
          HSBToRGB.Green = 0
          HSBToRGB.Blue = 0
     Else
          Delta = s * maxRGB / 255
          If h > 3 Then
               HSBToRGB.Blue = CByte(Round(maxRGB))
               If h > 4 Then
                    HSBToRGB.Green = CByte(Round(maxRGB - Delta))
                    HSBToRGB.Red = CByte(Round((h - 4) * Delta)) + HSBToRGB.Green
               Else
                    HSBToRGB.Red = CByte(Round(maxRGB - Delta))
                    HSBToRGB.Green = CByte(HSBToRGB.Red - Round((h - 4) * Delta))
               End If
          Else
               If h > 1 Then
                    HSBToRGB.Green = CByte(Round(maxRGB))
                    If h > 2 Then
                         HSBToRGB.Red = CByte(Round(maxRGB - Delta))
                         HSBToRGB.Blue = CByte(Round((h - 2) * Delta)) + HSBToRGB.Red
                    Else
                         HSBToRGB.Blue = CByte(Round(maxRGB - Delta))
                         HSBToRGB.Red = CByte(HSBToRGB.Blue - Round((h - 2) * Delta))
                    End If
               Else
                    If h > -1 Then
                         HSBToRGB.Red = CByte(Round(maxRGB))
                         If h > 0 Then
                              HSBToRGB.Blue = CByte(Round(maxRGB - Delta))
                              HSBToRGB.Green = CByte(Round(h * Delta)) + HSBToRGB.Blue
                         Else
                              HSBToRGB.Green = CByte(Round(maxRGB - Delta))
                              HSBToRGB.Blue = CByte(HSBToRGB.Green - Round(h * Delta))
                         End If
                    End If
               End If
          End If
     End If
End Function

それから、間違いがあったと投稿した人がいましたが、あまり詳しくはありませんでした

ただし、hが5を超える場合、たとえばR:130 G:65 B:111の色の場合は、管理する必要があると思います

If h > 5 Then
    HSBToRGB.Red = CByte(Round(maxRGB))
If h > 6 Then
    HSBToRGB.Blue= CByte(Round(maxRGB - Delta))
    HSBToRGB.Green= CByte(Round((h - 6) * Delta)) HSBToRGB.Blue
Else
    HSBToRGB.Green= CByte(Round(maxRGB - Delta))
    HSBToRGB.Blue = CByte(HSBToRGB.Green- Round((h - 6) * Delta))
End If

そのコードを追加する必要がありますか? そして、HSBからRGBに変換する必要があると思います(私のC#変換で)

...
if (s != 0) {
    delta = s * maxRGB / 255;
    if (h > 5)
        rgb.Red = Convert.ToByte(Math.Round(maxRGB));
    if (h > 6)
    {
        rgb.Green = Convert.ToByte(Math.Round(maxRGB - delta));
        rgb.Blue = Convert.ToByte(rgb.Green - Math.Round((h - 6) * delta));
    }
    if (h > 3)
    {
        ...

また、上記のようにする必要があります、または

if (h > 6) { } 
else if (h > 3)  { }
4

6 に答える 6

9

.NET の Color オブジェクトに組み込まれているメソッドを使用することは、いくつかの回答が指摘しているように、逆 (HSB カラーを RGB に変換する) をサポートしていないため、開始点ではありません。さらに、明るさ/値ではなく、Color.GetBrightness実際にはlightnessを返します。HSB/HSV と HSL の色空間は類似しているため、これらの色空間の違いについては多くの混乱があります ( Wikipedia )。間違ったアルゴリズムやモデルを使用してしまうカラー ピッカーがたくさんあります。

元のコードは、RGB カラーを指定して色相の値を計算するときに、考えられるいくつかのシナリオを見逃しているように見えます。あなたが考えているコードへの追加を追うのは少し難しいですが、私が最初に飛び出すこと(そしてあなたが修正を提案していないように見えること)は、彩度= 0の場合、色相を -1 にします。後で色相に 60 を掛けると -60 になり、それを 360 ( If h < 0 Then h = h + 360) に追加すると、結果は 300 になりますが、これは正しくありません。

次のコード (VB.NET) を使用して、RGB と HSB (私は HSV と呼んでいます) を変換します。結果は非常に広範囲にテストされており、Photoshop のカラー ピッカーによって得られる結果と事実上同じです (カラー プロファイルの補正は別として)。投稿されたコードと私の主な違い (色相を計算する重要な部分は別として) は、0 から 255 の間の元の値で作業するよりも、計算を行うために RGB 値を 0 から 1 の間に正規化することを好むことです。 . これにより、投稿した元のコードの非効率性と複数の変換の一部も解消されます。

Public Function RGBtoHSV(ByVal R As Integer, ByVal G As Integer, ByVal B As Integer) As HSV
     ''# Normalize the RGB values by scaling them to be between 0 and 1
     Dim red As Decimal = R / 255D
     Dim green As Decimal = G / 255D
     Dim blue As Decimal = B / 255D

     Dim minValue As Decimal = Math.Min(red, Math.Min(green, blue))
     Dim maxValue As Decimal = Math.Max(red, Math.Max(green, blue))
     Dim delta As Decimal = maxValue - minValue

     Dim h As Decimal
     Dim s As Decimal
     Dim v As Decimal = maxValue

     ''# Calculate the hue (in degrees of a circle, between 0 and 360)
     Select Case maxValue
        Case red
           If green >= blue Then
               If delta = 0 Then
                  h = 0
               Else
                  h = 60 * (green - blue) / delta
               End If
           ElseIf green < blue Then
               h = 60 * (green - blue) / delta + 360
           End If
        Case green
           h = 60 * (blue - red) / delta + 120
        Case blue
           h = 60 * (red - green) / delta + 240
     End Select

     ''# Calculate the saturation (between 0 and 1)
     If maxValue = 0 Then
        s = 0
     Else
        s = 1D - (minValue / maxValue)
     End If

     ''# Scale the saturation and value to a percentage between 0 and 100
     s *= 100
     v *= 100

  ''# Return a color in the new color space
  Return New HSV(CInt(Math.Round(h, MidpointRounding.AwayFromZero)), _
                 CInt(Math.Round(s, MidpointRounding.AwayFromZero)), _
                 CInt(Math.Round(v, MidpointRounding.AwayFromZero)))
End Function

HSB (私は HSV と呼んでいます) の色から RGB に変換するために使用するコードは投稿していませんが、0 から 1 の間の中間値を使用して、私が使用するものを次に示します。

Public Function HSVtoRGB(ByVal H As Integer, ByVal S As Integer, ByVal V As Integer) As RGB
     ''# Scale the Saturation and Value components to be between 0 and 1
     Dim hue As Decimal = H
     Dim sat As Decimal = S / 100D
     Dim val As Decimal = V / 100D

     Dim r As Decimal
     Dim g As Decimal
     Dim b As Decimal

     If sat = 0 Then
       ''# If the saturation is 0, then all colors are the same.
       ''# (This is some flavor of gray.)
        r = val
        g = val
        b = val
     Else
        ''# Calculate the appropriate sector of a 6-part color wheel
        Dim sectorPos As Decimal = hue / 60D
        Dim sectorNumber As Integer = CInt(Math.Floor(sectorPos))

        ''# Get the fractional part of the sector
        ''# (that is, how many degrees into the sector you are)
        Dim fractionalSector As Decimal = sectorPos - sectorNumber

        ''# Calculate values for the three axes of the color
        Dim p As Decimal = val * (1 - sat)
        Dim q As Decimal = val * (1 - (sat * fractionalSector))
        Dim t As Decimal = val * (1 - (sat * (1 - fractionalSector)))

        ''# Assign the fractional colors to red, green, and blue
        ''# components based on the sector the angle is in
        Select Case sectorNumber
           Case 0, 6
              r = val
              g = t
              b = p
           Case 1
              r = q
              g = val
              b = p
           Case 2
              r = p
              g = val
              b = t
           Case 3
              r = p
              g = q
              b = val
           Case 4
              r = t
              g = p
              b = val
           Case 5
              r = val
              g = p
              b = q
        End Select
     End If

     ''# Scale the red, green, and blue values to be between 0 and 255
     r *= 255
     g *= 255
     b *= 255

     ''# Return a color in the new color space
     Return New RGB(CInt(Math.Round(r, MidpointRounding.AwayFromZero)), _
                    CInt(Math.Round(g, MidpointRounding.AwayFromZero)), _
                    CInt(Math.Round(b, MidpointRounding.AwayFromZero)))
End Function

編集:このコードは、Richard J. Ross III によって C で提供されたものと非常によく似ています。オンラインで見つけられる限り多くの異なるアルゴリズムを探し出し、それぞれの最適な部分を借りて多くのコードを書き直し、広範なテストを行って結果の正確性を検証しました。これは私的なライブラリのためだけのものだったので、誰からコードを借りたかを書き留めることを怠りました. VB バージョンは、C からの変換をしたくない人に役立つかもしれません:-)

于 2010-11-08T13:55:36.520 に答える
2

これを行う方法についての私のバージョンは次のとおりです(Cでは、申し訳ありませんが、変換するのは難しくないはずです。 と を または int に置き換えるだけで、int *ポインターdouble *構文outref使用しないでください)

void colorlib_hsbtorgb(double hue, double saturation, double brightness, int *red, int *green, int *blue)
{   
    if (saturation == 0)
    {
        *red = *green = *blue = brightness;
    }
    else
    {
        // the color wheel consists of 6 sectors. Figure out which sector you're in.
        double sectorPos = hue / 60.0;
        int sectorNumber = (int)(floor(sectorPos));
        // get the fractional part of the sector
        double fractionalSector = sectorPos - sectorNumber;

        // calculate values for the three axes of the color. 
        double p = brightness * (1.0 - saturation);
        double q = brightness * (1.0 - (saturation * fractionalSector));
        double t = brightness * (1.0 - (saturation * (1 - fractionalSector)));

        // assign the fractional colors to r, g, and b based on the sector the angle is in.
        switch (sectorNumber)
        {
            case 0:
                *red = brightness;
                *green = t;
                *blue = p;
                break;
            case 1:
                *red = q;
                *green = brightness;
                *blue = p;
                break;
            case 2:
                *red = p;
                *green = brightness;
                *blue = t;
                break;
            case 3:
                *red = p;
                *green = q;
                *blue = brightness;
                break;
            case 4:
                *red = t;
                *green = p;
                *blue = brightness;
                break;
            case 5:
                *red = brightness;
                *green = p;
                *blue = q;
                break;
        }
    }
}

RGB から hsb:

void colorlib_rgbtohsb(int red, int green, int blue, double *hue, double *saturation, double *brightness)
{
    double dRed = red / 255;
    double dGreen = green / 255;
    double dBlue = blue / 255;

    double max = fmax(dRed, fmax(dGreen, dBlue));
    double min = fmin(dRed, fmin(dGreen, dBlue));

    double h = 0;
    if (max == dRed && dGreen >= dBlue)
    {
        h = 60 * (dGreen - dBlue) / (max - min);
    }
    else if (max == dRed && dGreen < dBlue)
    {
        h = 60 * (dGreen - dBlue) / (max - min) + 360;
    }
    else if (max == dGreen)
    {
        h = 60 * (dBlue - dRed) / (max - min) + 120;
    }
    else if (max == dBlue)
    {
        h = 60 * (dRed - dGreen) / (max - min) + 240;
    }

    double s = (max == 0) ? 0.0 : (1.0 - (min / max));

    *hue = h;
    *saturation = s;
    *brightness = max;
}

C# で自分のコードを見つけたら、この回答を編集します....

于 2010-11-08T13:01:20.877 に答える
1

Color GetBrightness、GetHue、および GetSaturation メソッドの使用についてはどうですか?

于 2010-11-08T13:04:54.197 に答える
1

.net を使用している場合、車輪を再発明する必要はありません。

Dim c = Color.FromArgb(myRed, myGreen, myBlue)
Dim h = c.GetHue()
Dim s = c.GetSaturation()
Dim b = c.GetBrightness()
于 2010-11-08T13:05:33.567 に答える
1

RGB から HSB への変換は、次のColor構造を使用して簡単に行うことができます。

Function RGBToHSB(rgb As RGBColor) As HSBColor
  Dim c As Color = Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue)
  RGBToHSB.Hue = c.GetHue()
  RGBToHSB.Saturation = c.GetSaturation()
  RGBToHSB.Brightness = c.GetBrightness()
End Function

ただし、その逆はサポートされていません。

于 2010-11-08T13:06:14.543 に答える
0

解決

輝度コンポーネントは、R、G、および B の最大値であるため、非常に簡単に計算できます (参照:ロチェスター工科大学の RGB から HSV への式)。255 で割り、スケールを掛けることで、好きなようにスケーリングできます。これは、既存のコードで行うのと同じです:

maxRGB = Max(Max(rgb.Red, rgb.Green), rgb.Blue)
b = maxRGB    
...    
RGBToHSB.Brightness = b * 100 / 255

したがって、最終的には、組み込みの .Net 関数を使用して、明るさを計算するだけです。完全なコードは次のようになります(タイプを除く):

Function RGBToHSB(rgb As RGBColor) As HSBColor
  Dim maxRGB As Double
  maxRGB = Max(Max(rgb.Red, rgb.Green), rgb.Blue)

  Dim c As Color = Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue)
  RGBToHSB.Hue = c.GetHue()
  RGBToHSB.Saturation = c.GetSaturation() * 100
  RGBToHSB.Brightness = maxRGB * 100 / 255
End Function

HSBについて少し(HSVと同じ)

ダレル・レックス・フィンリー より:

HSV (HSB とも呼ばれます) システムでは、色の明るさはその V 成分です。その成分は、色の 3 つの RGB 成分のいずれかの最大値として単純に定義されます。他の 2 つの RGB 成分は、V を決定するときに無視されます。

のMicrosoftドキュメントによるとColor.GetBrightness

この Color 構造体の色相、彩度、明るさ ( HSB ) の明るさの値を取得します。

MSDNブログのこのようなHSLを意味する場合、MSDNはHSBを使用するという言及をいくつか見つけました(コメントを参照)。簡単なテストで、これが正しいことが証明されます (C# の場合):

// Define a color which gives different HSL and HSB value
Color c = Color.FromArgb(255, 0, 0);
// Get the brightness, scale it from 0.0 - 1.0 up to 0 - 255
int bright = (int)(c.GetBrightness() * 255.00);
// Output it
Console.WriteLine(bright.ToString());

127これは、明らかに HSL である の値になります。HSB の場合、値は RG と B の最大値 (つまり255) である必要があります。

于 2010-11-08T14:12:03.280 に答える