سؤال

قرأت المقال خوارزمية للتبديل بين قيم ألوان 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)  { }
هل كانت مفيدة؟

المحلول

يعد استخدام الأساليب المضمنة في كائن ملون .NET غير متابع لأنه ، كما تشير العديد من الإجابات ، فهي لا تدعم العكس (تحويل لون HSB إلى RGB). بالإضافة إلى ذلك، Color.GetBrightness في الواقع يعود خفة, ، بدلا من السطوع/القيمة. هناك الكثير من الالتباس حول الاختلافات بين مساحات ألوان HSB/HSV و HSL بسبب أوجه التشابه عنها (ويكيبيديا). أرى الكثير من ملتمي الألوان الذين ينتهي بهم المطاف باستخدام الخوارزمية و/أو النموذج الخاطئ.

يبدو لي الرمز الأصلي وكأنه يفتقد بعض السيناريوهات الممكنة عندما يحسب القيمة لـ Hue ، بالنظر إلى لون RGB. من الصعب بعض الشيء بالنسبة لي أن أتابع الإضافات التي تفكر فيها في الكود ، ولكن أول ما يقفز في وجهي (ولا يبدو أنك تقترح التصحيح) هو أنه عندما تشبع = 0 ، قمت بتعيين هوى إلى -1. عندما تتكاثر في وقت لاحق من اللون 60 ، ينتهي بك الأمر بـ -60 ، ثم تضيف ذلك إلى 360 (If h < 0 Then h = h + 360) ، إنتاج نتيجة 300 ، وهذا غير صحيح.

يمكنني استخدام الكود التالي (في VB.NET) للتحويل بين RGB و HSB (والتي أسميها HSV). لقد تم اختبار النتائج على نطاق واسع للغاية ، وتتطابق النتائج تقريبًا مع تلك التي قدمها منتقي ألوان Photoshop (بصرف النظر عن التعويض الذي يقوم به لملفات تعريف الألوان). الفرق الرئيسي بين الكود المنشور واللغمي (بصرف النظر عن الجزء المهم الذي يحسب هيو) هو أنني أفضل تطبيع قيم RGB بين 0 و 1 للقيام بالحسابات ، بدلاً من العمل مع القيم الأصلية بين 0 و 255 هذا يلغي بعض أوجه القصور والتحويلات المتعددة في الكود الأصلي الذي نشرته أيضًا.

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

تعديل: يبدو هذا الرمز مشابهًا جدًا لتلك المقدمة في C من قبل Richard J. Ross III. لقد بحثت عن العديد من الخوارزميات المختلفة التي يمكنني العثور عليها عبر الإنترنت ، وأعيد كتابة الكثير من التعليمات البرمجية التي تستعير الأفضل من كل منها ، وأجرت اختبارات واسعة للتحقق من دقة النتائج. لقد أهملت أن ألاحظ من قمت باستعارة التعليمات البرمجية ، حيث كان هذا فقط لمكتبة خاصة. ربما يساعد إصدار VB شخصًا لا يريد إجراء تحويل من C. :-)

نصائح أخرى

إليك نسختي حول كيفية القيام بذلك (في C ، آسف ، ولكن لا ينبغي تحويلها ، فقط استبدال int *'رمل double *مع out أو ref ints ، ولا تستخدم بناء جملة المؤشر)

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#، فسوف أقوم بتحرير هذه الإجابة ....

ماذا عن استخدام أساليب اللون ، Gethue و GetTuration؟

إذا كنت تستخدم .NET ، لماذا إعادة اختراع العجلة؟

Dim c = Color.FromArgb(myRed, myGreen, myBlue)
Dim h = c.GetHue()
Dim s = c.GetSaturation()
Dim b = c.GetBrightness()

يجب أن يكون التحويل من 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

لا يدعم العكس.

المحلول

يمكنك حساب مكون السطوع ببساطة تمامًا لأنه الحد الأقصى لـ 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. يتم تعريف هذا المكون ببساطة على أنه الحد الأقصى لقيمة أي من مكونات RGB الثلاثة من اللون - يتم تجاهل المكونين الآخرين RGB عند تحديد V.

وفقا ل وثائق Microsoft إلى عن على Color.GetBrightness:

يحصل على سحر اللون (HSB) قيمة السطوع لهذا بنية اللون.

لقد وجدت بعض المراجع التي تقول إن MSDN يستخدم HSB عندما يعني HSL مثل هذا واحد من مدونات MSDN (انظر التعليقات). يثبت الاختبار السريع أن هذا صحيح (في 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).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top