色轮背后的数学原理是什么?

19
我想创建一个有12片的馅饼图,每一片都是不同的颜色。
几乎每个色轮似乎都遵循相同的格式;例如: http://www.tigercolor.com/color-lab/color-theory/color-theory-intro.htm
但是有什么算法可以生成这些颜色呢?RGB(theta)背后的数学是什么?肯定有一些已经建立的科学理论,但是谷歌并没有给我任何线索。

1
这不就是 color(angle) = HSV(angle, 1, 1) 吗? - Andreas Rejbrand
好的...但是这背后的数学原理是什么? - P i
2
你看过https://dev59.com/EXVD5IYBdhLWcg3wXamd上的答案吗? - nate c
请考虑给StackExchange Color Theory网站点赞/关注:https://area51.stackexchange.com/proposals/110687/color-theory - Adi Shavit
5个回答

12

看一下http://www.easyrgb.com,它有很多颜色转换算法。这里是RGB转HSV的算法。

var_R = ( R / 255 )                     //RGB from 0 to 255
var_G = ( G / 255 )
var_B = ( B / 255 )

var_Min = min( var_R, var_G, var_B )    //Min. value of RGB
var_Max = max( var_R, var_G, var_B )    //Max. value of RGB
del_Max = var_Max - var_Min             //Delta RGB value 

V = var_Max

if ( del_Max == 0 )                     //This is a gray, no chroma...
{
   H = 0                                //HSV results from 0 to 1
   S = 0
}
else                                    //Chromatic data...
{
   S = del_Max / var_Max

   del_R = ( ( ( var_Max - var_R ) / 6 ) + ( del_Max / 2 ) ) / del_Max
   del_G = ( ( ( var_Max - var_G ) / 6 ) + ( del_Max / 2 ) ) / del_Max
   del_B = ( ( ( var_Max - var_B ) / 6 ) + ( del_Max / 2 ) ) / del_Max

   if      ( var_R == var_Max ) H = del_B - del_G
   else if ( var_G == var_Max ) H = ( 1 / 3 ) + del_R - del_B
   else if ( var_B == var_Max ) H = ( 2 / 3 ) + del_G - del_R

   if ( H < 0 ) H += 1
   if ( H > 1 ) H -= 1
}

8
一个颜色轮(例如Mac OS X的颜色选择器,如下图所示)显示了色相和饱和度(HSV色彩空间中的两个组成部分)。色相随角度变化,饱和度随半径变化。通常还有一个用于值(也称亮度)的单独滑块。

Mac OS X colour picker

请参阅维基百科了解如何在HSV和RGB之间进行转换。或者您所选的编程语言可能有API。例如,Python具有colorsys


7

我在Excel VBA中尝试制作基于HSL的色轮,使用小单元格作为“像素”,效果不错,所以我想分享一下。

这展示了如何在编程中转换HSL和RGB,以及如何以编程方式在任何网格上绘制线条/圆形,甚至是电子表格单元格。

代码已经准备就绪,可以直接运行:

Option Explicit

Const colorSheetName = "COLORS"
Const pi = 3.14159265358979
Const squareSize = 3.75             'cell square size (pts)
Const cDiameter = 80#               'circle diameter (cells)
Const numAngles = 360#              'number of angles (lines to draw)

Sub CalculateColorWheel()
  Dim ws As Worksheet, radsPerAngle As Double, radius As Long, xStop As Double, _
      yStop As Double, z As Integer, xyLength As Double, lineDot As Long, _ 
      lineLength As Long, h As Byte, s As Byte, v As Byte, r As Byte, g As Byte, b As Byte

    Set ws = ThisWorkbook.Sheets.Add                        'create new worksheet
    On Error Resume Next                                    'ignore error 
    Application.DisplayAlerts = False                       'ignore warning
    ThisWorkbook.Sheets(colorSheetName).Delete              'delete worksheet (if exists)
    Application.DisplayAlerts = True                        'stop ignoring warnings
    On Error GoTo 0                                         'stop ignoring errors

    With ws
        .Name = colorSheetName                              'name the new sheet
        .Rows.RowHeight = squareSize                        'set rowheight 
        .Columns.ColumnWidth=widthToColumnWidth(squareSize) 'match columnwidth to row
        ActiveWindow.DisplayGridlines = False               'hide gridlines
        ActiveWindow.DisplayHeadings = False                'hide row/col headings
        radius = cDiameter / 2                              'centre point
        lineLength = radius * 1.5                           'dots per angle (line)
        radsPerAngle = (360 / numAngles) * pi / 180         'radians=a(degrees)×pi÷180°

        Debug.Print "Grid size=" & .[a1].Height & "×" & .[a1].Width _
                & ", Diameter:" & cDiameter _
                & ", Area=" & Round(pi * radius ^ 2, 0) _
                & ", Circumference=" & Round(2 * pi * radius, 0) _
                & ", Radians per Angle=" & Round(radsPerAngle, 3) _
                & " × " & numAngles & " angles"                 'stats

        For z = 0 To numAngles - 1  'loop through each angle
            For lineDot = 1 To lineLength   'loop thru length of line
                xyLength = radius * (lineDot / lineLength)  'calc dot xy& offset top-left
                xStop = Int(Cos(radsPerAngle * z) * xyLength) + radius + 2 'x (column)
                yStop = Int(Sin(radsPerAngle * z) * xyLength) + radius + 2 'y (row)

                If .Cells(yStop, xStop).Interior.Pattern=xlNone Then 'skip colored cells
                    h = ((z + 1) / numAngles) * 255                'hue=angle
                    s = (lineDot / lineLength) * 255                    'saturation=radius
                    v = 255                    'maximum brightness. (Adjustable)
                    HSVtoRGB h, s, v, r, g, b                      'convert HSV to RGB
                    .Cells(yStop, xStop).Interior.Color=rgb(r,g,b) 'color the cell
                    dots = dots + 1
                End If
            Next lineDot
            Application.StatusBar = Format(z / (numAngles - 1), "0%") 
            DoEvents    'don't lag
        Next z
    End With
    Beep
    Application.StatusBar = "Finished drawing color circle (" & dots & " colors)"
End Sub

Public Function widthToColumnWidth(pts As Double) As Double
'convert desired column width (points) to Excel "ColWidthUnits"
'12pts and under is a 1:12 ratio of (colWidthUnits:Pts).
' Over 12pts: 1:12 for 1st unit, then 1:(75/11) for remainder
    Select Case pts
        Case Is <= 0:  widthToColumnWidth = 0
        Case Is <= 12: widthToColumnWidth = pts / 12
        Case Else:     widthToColumnWidth = 1 + (pts - 12) / (75 / 11) '
    End Select
End Function

Public Sub HSVtoRGB(h As Byte, s As Byte, v As Byte, r As Byte, g As Byte, b As Byte)
    Dim minV As Byte, maxV As Byte, Chroma As Byte, tempH As Double

    If v = 0 Then
        r = 0: g = 0: b = 0
    Else
        If s = 0 Then
            r = v: g = v: b = v:
        Else
            maxV = v: Chroma = s / 255 * maxV: minV = maxV - Chroma
            Select Case h
                Case Is >= 170: tempH = (h - 170) / 43: g = 0
                                If tempH < 1 Then
                                    b = maxV: r = maxV * tempH
                                Else: r = maxV: b = maxV * (2 - tempH): End If
                Case Is >= 85:  tempH = (h - 85) / 43: r = 0
                                If tempH < 1 Then
                                    g = maxV: b = maxV * tempH
                                Else: b = maxV: g = maxV * (2 - tempH): End If
                Case Else:      tempH = h / 43: b = 0
                                If tempH < 1 Then
                                    r = maxV: g = maxV * tempH
                                Else: g = maxV: r = maxV * (2 - tempH): End If
            End Select
            r = r / maxV * (maxV - minV) + minV
            g = g / maxV * (maxV - minV) + minV
            b = b / maxV * (maxV - minV) + minV
        End If
    End If
End Sub

如何在Excel中运行此代码:将上面的代码复制并粘贴到普通模块中。(选择代码,按Ctrl+C进行复制,然后在Excel中,按住Alt并按F11+I+M,然后按Ctrl+V进行粘贴,最后按F5运行。)


更多信息:


2

如果您想要一个像您提供的示例(以及大多数手工艺品店油漆部分中找到的大多数颜色轮)一样的颜色轮,其中红色与绿色相对,蓝色与黄橙色相对,紫色相对于黄色等等,您可以简单地进行以下数学计算来修改HSL或HSV的色调,以获得传统的色调...

double ToLegacyHue(double modernHue) {
   modernHue = ((modernHue % 360) + 360) % 360; // normalize 360 > modernHue >= 0
   double ret = 0;
   if(modernHue < 60) {
      ret = modernHue * 2;
   } else if(modernHue < 120) {
      ret = modernHue + 60;
   } else {
      ret = (modernHue - 120) * 0.75 + 180;
   }
   return ret;
}

double FromLegacyHue(double legacyHue) {
   legacyHue = ((legacyHue % 360) + 360) % 360; // normalize 360 > legacyHue >= 0
   double ret = 0;
   if(legacyHue < 120) {
      ret = legacyHue / 2;
   } else if(legacyHue < 180) {
      ret = legacyHue - 60;
   } else {
      ret = (legacyHue - 180) / 0.75 + 120;
   }
   return ret;
}

0

绘制色轮的步骤:

  • 取一个笛卡尔坐标系下的点:(x,y)
  • 将笛卡尔坐标转换为极坐标:(x,y) -> (r,theta)
  • 将坐标转换为颜色 = 将极坐标转换为HSV:(r,theta) -> (h,s,v)

其中:

  • theta = 色调
  • 半径 r = 饱和度
  • 值为常数

这里有一个工具漂亮的例子


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接