【问题标题】:LAB, RGB, XYZ Color Conversion Incorrect Vice versaLAB、RGB、XYZ 颜色转换不正确,反之亦然
【发布时间】:2018-11-07 23:21:23
【问题描述】:

我正在为一个项目制作自定义颜色选择器,它是 Photoshop 风格的,我让所有其他转换按预期工作,但我无法让 RGBToLAB 和 LABToRGB 正常工作。

问题不仅在于颜色没有正确表示,而且转换也不完美。

示例:

  • 实验室_58:0:0
  • XYZ_0.25960986510312:0.25960986510312:0.25960986510312
  • RGB_{R:10 G:8 B:7 A:255}
  • XYZ_0.250358161840588:5.51162077338675:66.3836625496266
  • 实验室_85.3739502460609:0:0

最初的LAB和最后的LAB不一样,说明转换有缺陷。我不仅得到了错误的颜色,而且值也发生了变化,尤其是当 LAB.L 假设为常数时(在本例中,因为这是滑块当前正在控制的)

上面的 LAB->RGB->LAB 转换有缺陷,但 XYZ->RGB->XYZ 转换也有缺陷。

显然我对转换 LABToLAB 不感兴趣,但上面确实指出了转换中的一个缺陷。

我尝试过的事情:

  1. This formula on wikipedia

  2. EasyRGB's code

  3. This javascript code on github

  4. This cginc code intended for unity, which is where i'm at now

       Private Function LABToXYZ(LAB As LAB) As XYZ
        Dim X, Y, Z As New Double
    
        Y = ((LAB.L + 16.0) / 116.0)
        X = ((LAB.A / 500.0) + Y)
        Z = (Y - (LAB.B / 200.0))
    
        Dim Less = 0.206897
    
        If (X > Less) Then
            X = Math.Pow(X, 3)
        Else
            X = ((X - 16.0 / 116.0) / 7.787)
        End If
        If (Y > Less) Then
            Y = Math.Pow(Y, 3)
        Else
            Y = ((Y - 16.0 / 116.0) / 7.787)
        End If
        If (Z > Less) Then
            Z = Math.Pow(Z, 3)
        Else
            Z = ((Z - 16.0 / 116.0) / 7.787)
        End If
    
        Return New XYZ(X, Y, Z)
    End Function
    
    Private Function XYZToRGB(XYZ As XYZ) As Color
        Dim R, G, B As New Double
        Dim X, Y, Z As New Double
    
        X = (XYZ.X / 100)
        Y = (XYZ.Y / 100)
        Z = (XYZ.Z / 100)
    
        R = ((X * 3.2406) + (Y * -1.5372) + (Z * -0.4986))
        G = ((X * -0.9689) + (Y * 1.8758) + (Z * 0.0415))
        B = ((X * 0.0557) + (Y * -0.204) + (Z * 1.057))
    
        Dim Less As Double = 0.0031308
    
        If (R > Less) Then
            X = ((1.055 * Math.Pow(R, (1.0 / 2.4))) - 0.055)
        Else
            X = (R * 12.92)
        End If
        If (G > Less) Then
            Y = ((1.055 * Math.Pow(G, (1.0 / 2.4))) - 0.055)
        Else
            Y = (G * 12.92)
        End If
        If (B > Less) Then
            Z = ((1.055 * Math.Pow(B, (1.0 / 2.4))) - 0.055)
        Else
            Z = (B * 12.92)
        End If
    
        Return New Color(CSng(X), CSng(Y), CSng(Z))
    End Function
    
    Private Function RGBToXYZ(Color As Color) As XYZ
        Dim RGB = ColorToRGB(Color)
        Dim X, Y, Z As New Double
        Dim Less As Double = 0.04045
    
        If (RGB.R > Less) Then
            X = Math.Pow(((RGB.R + 0.055) / 1.055), 2.4)
        Else
            X = (RGB.R / 12.92)
        End If
        If (RGB.G > Less) Then
            Y = Math.Pow(((RGB.G + 0.055) / 1.055), 2.4)
        Else
            Y = (RGB.G / 12.92)
        End If
        If (RGB.B > Less) Then
            Z = Math.Pow(((RGB.B + 0.055) / 1.055), 2.4)
        Else
            Z = (RGB.B / 12.92)
        End If
    
        X = (((X * 0.4124) + (Y * 0.3576) + (Z * 0.1805)) * 100.0)
        Y = (((X * 0.2126) + (Y * 0.7152) + (Z * 0.0722)) * 100.0)
        Z = (((X * 0.0193) + (Y * 0.1192) + (Z * 0.9505)) * 100.0)
    
        Return New XYZ(X, Y, Z)
    End Function
    
    Private Function XYZToLAB(XYZ As XYZ) As LAB
        Dim X, Y, Z As New Double
        Dim L, A, B As New Double
        Dim Less As Double = 0.008856
    
        X = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
        Y = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
        Z = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
    
        If (X > Less) Then
            X = Math.Pow(X, (1.0 / 3.0))
        Else
            X = ((7.787 * X) + (16.0 / 116.0))
        End If
        If (Y > Less) Then
            Y = Math.Pow(Y, (1.0 / 3.0))
        Else
            Y = ((7.787 * Y) + (16.0 / 116.0))
        End If
        If (Z > Less) Then
            Z = Math.Pow(Z, (1.0 / 3.0))
        Else
            Z = ((7.787 * Z) + (16.0 / 116.0))
        End If
    
        L = ((116.0 * Y) - 16.0)
        A = (500.0 * (X - Y))
        B = (200.0 * (Y - Z))
    
        Return New LAB(L, A, B)
    End Function
    
    Function ColorToRGB(Color As Color) As RGB
        Return New RGB((Convert.ToInt32(Color.R) / 255), (Convert.ToInt32(Color.G) / 255), (Convert.ToInt32(Color.B) / 255))
    End Function
    Public Class RGB
    Public ReadOnly Min As Double = 0
    Public ReadOnly Max As Double = 1
    
    Public Sub New()
    End Sub
    
    Public Sub New(R As Double, G As Double, B As Double)
        Me.R = R
        Me.G = G
        Me.B = B
    End Sub
    
    Public Sub New(Color As Color)
        Me.R = (Convert.ToInt32(Color.R) / 255)
        Me.G = (Convert.ToInt32(Color.G) / 255)
        Me.B = (Convert.ToInt32(Color.B) / 255)
    End Sub
    
    Private _R As New Double
    Private _G As New Double
    Private _B As New Double
    
    Public Property R As Double
        Get
            Return _R
        End Get
        Set
            _R = LimitInRange(Value, Min, Max)
        End Set
    End Property
    
    Public Property G As Double
        Get
            Return _G
        End Get
        Set
            _G = LimitInRange(Value, Min, Max)
        End Set
    End Property
    
    Public Property B As Double
        Get
            Return _B
        End Get
        Set
            _B = LimitInRange(Value, Min, Max)
        End Set
    End Property
    
    Overrides Function ToString() As String
        Return (_R.ToString & ":"c & _G.ToString & ":"c & _B.ToString)
    End Function
    End Class
    
    Public Class XYZ
    Public ReadOnly Min As Double = 0
    Public ReadOnly Max As Double = 100
    
    Public Sub New()
    End Sub
    
    Public Sub New(X As Double, Y As Double, Z As Double)
        Me.X = X
        Me.Y = Y
        Me.Z = Z
    End Sub
    
    Private _X As New Double
    Private _Y As New Double
    Private _Z As New Double
    
    Public Property X As Double
        Get
            Return _X
        End Get
        Set
            _X = LimitInRange(Value, Min, Max)
        End Set
    End Property
    
    Public Property Y As Double
        Get
            Return _Y
        End Get
        Set
            _Y = LimitInRange(Value, Min, Max)
        End Set
    End Property
    
    Public Property Z As Double
        Get
            Return _Z
        End Get
        Set
            _Z = LimitInRange(Value, Min, Max)
        End Set
    End Property
    
    Overrides Function ToString() As String
        Return (_X.ToString & ":"c & _Y.ToString & ":"c & _Z.ToString)
    End Function
    End Class
    
    Public Class LAB
    Public ReadOnly Min As Double = -128
    Public ReadOnly Max As Double = 127
    
    Sub New()
    End Sub
    
    Sub New(L As Double, A As Double, B As Double)
        Me.L = L
        Me.A = A
        Me.B = B
    End Sub
    
    Private _L As New Double
    Private _A As New Double
    Private _B As New Double
    
    Property L As Double
        Get
            Return _L
        End Get
        Set
            _L = LimitInRange(Value, 0, 100)
        End Set
    End Property
    
    Property A As Double
        Get
            Return _A
        End Get
        Set
            _A = LimitInRange(Value, Min, Max)
        End Set
    End Property
    
    Property B As Double
        Get
            Return _B
        End Get
        Set
            _B = LimitInRange(Value, Min, Max)
        End Set
    End Property
    
    Overrides Function ToString() As String
        Return (_L.ToString & ":"c & _A.ToString & ":"c & _B.ToString)
    End Function
    End Class
    
    Function LimitInRange(Value As Double, Min As Double, Max As Double) As Double
        Select Case Value
            Case <= Min
                Return Min
            Case >= Max
                Return Max
            Case Else
                Return Value
        End Select
    End Function
    

我需要 VB.Net 中的代码,这就是为什么我正在为我的项目转换和调整统一代码,但是我被困住了,需要一些帮助。

如果有人知道我做错了什么,我会很乐意倾听。

更新 1: 我试图通过不匹配两种转换方法来更多地纠正转换,我越来越接近完美的转换,但是我担心我可能已经从这个问题上工作了这么久。

示例:

  • 实验室_0:0:0
  • XYZ_0.262413383082537:0.262413383082537:0.262413383082537
  • RGB _ {R:10 G:8 B:7 A:255}
  • XYZ_0.250358161840588:0.253536089358344:0.236754082437929
  • 实验室_2.29017121228677:-0.12373260790384:0.261362975778545

如您所见,问题比以前少了,但仍然存在。

    Private Function LABToXYZ(LAB As LAB) As XYZ
        Dim X, Y, Z As New Double

        Y = ((LAB.L + 16.0) / 116.0)
        X = ((LAB.A / 500.0) + Y)
        Z = (Y - (LAB.B / 200.0))

        Dim Less = 0.008856

        If (X > Less) Then
            X = Math.Pow(X, 3)
        Else
            X = ((X - 16.0 / 116.0) / 7.787)
        End If
        If (Y > Less) Then
            Y = Math.Pow(Y, 3)
        Else
            Y = ((Y - 16.0 / 116.0) / 7.787)
        End If
        If (Z > Less) Then
            Z = Math.Pow(Z, 3)
        Else
            Z = ((Z - 16.0 / 116.0) / 7.787)
        End If

        Return New XYZ(X * 100, Y * 100, Z * 100)
    End Function

    Private Function XYZToRGB(XYZ As XYZ) As Color
        Dim R, G, B As New Double
        Dim X, Y, Z As New Double

        X = (XYZ.X / 100)
        Y = (XYZ.Y / 100)
        Z = (XYZ.Z / 100)

        R = ((X * 3.2406) + (Y * -1.5372) + (Z * -0.4986))
        G = ((X * -0.9689) + (Y * 1.8758) + (Z * 0.0415))
        B = ((X * 0.0557) + (Y * -0.204) + (Z * 1.057))

        Dim Less As Double = 0.0031308

        If (R > Less) Then
            R = ((1.055 * Math.Pow(R, (1.0 / 2.4))) - 0.055)
        Else
            R = (R * 12.92)
        End If
        If (G > Less) Then
            G = ((1.055 * Math.Pow(G, (1.0 / 2.4))) - 0.055)
        Else
            G = (G * 12.92)
        End If
        If (B > Less) Then
            B = ((1.055 * Math.Pow(B, (1.0 / 2.4))) - 0.055)
        Else
            B = (B * 12.92)
        End If

        Return New Color(CSng(R), CSng(G), CSng(B))
    End Function

    Private Function RGBToXYZ(Color As Color) As XYZ
        Dim RGB = ColorToRGB(Color)
        Dim X, Y, Z As New Double
        Dim R, G, B As New Double
        Dim Less As Double = 0.04045

        If (RGB.R > Less) Then
            r = Math.Pow(((RGB.R + 0.055) / 1.055), 2.4)
        Else
            R = (RGB.R / 12.92)
        End If
        If (RGB.G > Less) Then
            G = Math.Pow(((RGB.G + 0.055) / 1.055), 2.4)
        Else
            G = (RGB.G / 12.92)
        End If
        If (RGB.B > Less) Then
            B = Math.Pow(((RGB.B + 0.055) / 1.055), 2.4)
        Else
            B = (RGB.B / 12.92)
        End If

        R *= 100
        G *= 100
        B *= 100

        X = ((R * 0.4124) + (G * 0.3576) + (B * 0.1805))
        Y = ((R * 0.2126) + (G * 0.7152) + (B * 0.0722))
        Z = ((R * 0.0193) + (G * 0.1192) + (B * 0.9505))

        Return New XYZ(X, Y, Z)
    End Function

    Private Function XYZToLAB(XYZ As XYZ) As LAB
        Dim X, Y, Z As New Double
        Dim L, A, B As New Double
        Dim Less As Double = 0.008856

        X = XYZ.X / 100
        Y = XYZ.Y / 100
        Z = XYZ.Z / 100

        If (X > Less) Then
            X = Math.Pow(X, (1.0 / 3.0))
        Else
            X = ((7.787 * X) + (16.0 / 116.0))
        End If
        If (Y > Less) Then
            Y = Math.Pow(Y, (1.0 / 3.0))
        Else
            Y = ((7.787 * Y) + (16.0 / 116.0))
        End If
        If (Z > Less) Then
            Z = Math.Pow(Z, (1.0 / 3.0))
        Else
            Z = ((7.787 * Z) + (16.0 / 116.0))
        End If

        L = ((116.0 * Y) - 16.0)
        A = (500.0 * (X - Y))
        B = (200.0 * (Y - Z))

        Return New LAB(L, A, B)
    End Function

更新 2: 进一步的测试显示 XNA.Framework.Color 中存在异常不受欢迎的行为,导致任何分数都被解释为 %。 这意味着 200.10 将超过最大颜色值 (255) 的 200%,这会将其限制为最大值 (255),因此除非您指定整数,否则最终可能会得到非常错误的输出。

我试图与代码 from this example as well 不匹配。我觉得我在进步,即使我不得不在转换中使用 XNA.Framework.Color 类。

如果我找到最终解决方案,我会更新。

更新 3: 在线测试here(source code here)和here显示我的LABToXYZ不正确。

我的结果:

  • 实验室_100:0:0
  • XYZ_95.047:100:100

他们的结果:

  • 实验室_100:0:0
  • XYZ_95.05:100:108.88

    Public Function LABtoXYZ(LAB As LAB) As XYZ
        Dim X, Y, Z As Double
        Y = ((LAB.L + 16.0) / 116.0)
        X = ((LAB.A / 500.0) + Y)
        Z = (Y - (LAB.B / 200.0))
    
        Dim Pow_X = Math.Pow(X, 3.0)
        Dim Pow_Y = Math.Pow(Y, 3.0)
        Dim Pow_Z = Math.Pow(Z, 3.0)
    
        Dim Less = 216 / 24389
    
        If (Pow_X > Less) Then
            X = Pow_X
        Else
            X = ((X - (16.0 / 116.0)) / 7.787)
        End If
        If (Pow_Y > Less) Then
            Y = Pow_Y
        Else
            Y = ((Y - (16.0 / 116.0)) / 7.787)
        End If
        If (Pow_Z > Less) Then
            Z = Pow_Z
        Else
            Z = ((Z - (16.0 / 116.0)) / 7.787)
        End If
    
        Return New XYZ((X * 95.047), (Y * 100.0), (Z * 108.883))
    End Function
    

但是用全 0 做 LAB 会导致 XYZ 全为 0,这是正确的行为,我不知道出了什么问题,是 Z 不正确,但我的代码中的错误在哪里?

更多示例 here 似乎表明我的代码是正确的,但我仍然得到一个不正确的 Z。

更新 4: 进一步细化和重做所有代码,我发现对 here 的示例进行转换和改编,给了我想要的结果,即使这些示例中有一些错误,值得注意的是 ^2.2 时应该是^2.4。

我还发现了一些精度问题,必须将双精度数转换为整数才能完美转换,但这可能是最后一次更新,除非我遇到任何问题,否则我会在继续时将这个问题留待一段时间在实践中测试代码。 当我确信代码没有缺陷时,我会回来并将其标记为已回答。

示例: 测试 1

  • 实验室_1:0:0
  • XYZ_0.105222895807779:0.110706172533356:0.120540201839494
  • RGB _ 4:4:4:255
  • XYZ_0.115400959145268:0.121410793419535:0.132216354033874
  • 实验室_1:0:0

测试 2

  • 实验室_10:0:0
  • XYZ_1.07024816003116:1.12601992701628:1.22604427713313
  • RGB _ 27:27:27:255
  • XYZ_1.04175693531671:1.09600940064882:1.19355423730657
  • 实验室_10:0:0

测试 3

  • 实验室_100:0:0
  • XYZ_95.047:100:108.883
  • RGB _ 255:255:255:255
  • XYZ_95.05:100:108.9
  • 实验室_100:0:0

测试 4

  • 实验室_11:0:0
  • XYZ_1.19854884694432:1.26100649883144:1.37302170612264
  • RGB _ 29:29:29:255
  • XYZ_1.16783071832485:1.22864883569159:1.33799858206814
  • 实验室_11:0:0

如上所示,如果不进行四舍五入,会有一个微小的变化会导致不完美的转换。

类

  Public Class RGB
        Public ReadOnly Min As Double = 0.0
        Public ReadOnly Max As Double = 255.0

        Public Sub New()
        End Sub

        Public Sub New(R As Integer, G As Integer, B As Integer)
            Me.R = R
            Me.G = G
            Me.B = B
        End Sub

        Public Sub New(R As Integer, G As Integer, B As Integer, A As Integer)
            Me.R = R
            Me.G = G
            Me.B = B
            Me.A = A
        End Sub
        Public Sub New(R As Double, G As Double, B As Double, A As Double)
            Me.R = Convert.ToInt32(R)
            Me.G = Convert.ToInt32(G)
            Me.B = Convert.ToInt32(B)
            Me.A = Convert.ToInt32(A)
        End Sub
        Public Sub New(R As Double, G As Double, B As Double)
            Me.R = Convert.ToInt32(R * 255)
            Me.G = Convert.ToInt32(G * 255)
            Me.B = Convert.ToInt32(B * 255)
        End Sub
        Public Sub New(Color As Color)
            Me.R = Convert.ToInt32(Color.R)
            Me.G = Convert.ToInt32(Color.G)
            Me.B = Convert.ToInt32(Color.B)
            Me.A = Convert.ToInt32(Color.A)
        End Sub

        Private _R As New Double
        Private _G As New Double
        Private _B As New Double
        Private _A As Double = 255

        Public Property R As Double
            Get
                Return _R
            End Get
            Set
                _R = LimitInRange(Value, Min, Max)
            End Set
        End Property

        Public Property G As Double
            Get
                Return _G
            End Get
            Set
                _G = LimitInRange(Value, Min, Max)
            End Set
        End Property

        Public Property B As Double
            Get
                Return _B
            End Get
            Set
                _B = LimitInRange(Value, Min, Max)
            End Set
        End Property

        Public Property A As Double
            Get
                Return _A
            End Get
            Set
                _A = LimitInRange(Value, Min, Max)
            End Set
        End Property

        Overrides Function ToString() As String
            Return (_R.ToString & ":"c & _G.ToString & ":"c & _B.ToString & ":"c & _A.ToString)
        End Function

        Public Shared Operator =(Left As RGB, Right As RGB) As Boolean
            If ((Left.R = Right.R) AndAlso (Left.G = Right.G) AndAlso (Left.B = Right.B) AndAlso (Left.A = Right.A)) Then
                Return True
            Else
                Return False
            End If
        End Operator

        Public Shared Operator <>(Left As RGB, Right As RGB) As Boolean
            Return (Not (Left = Right))
        End Operator

    End Class

    Public Class XYZ
        Public ReadOnly Min As Double = 0

        Public Sub New()
        End Sub

        Public Sub New(X As Double, Y As Double, Z As Double)
            Me.X = X
            Me.Y = Y
            Me.Z = Z
        End Sub

        Private _X As New Double
        Private _Y As New Double
        Private _Z As New Double

        Public Property X As Double
            Get
                Return _X
            End Get
            Set
                _X = LimitInRange(Value, Min, 95.05)
            End Set
        End Property

        Public Property Y As Double
            Get
                Return _Y
            End Get
            Set
                _Y = LimitInRange(Value, Min, 100)
            End Set
        End Property

        Public Property Z As Double
            Get
                Return _Z
            End Get
            Set
                _Z = LimitInRange(Value, Min, 108.9)
            End Set
        End Property

        Overrides Function ToString() As String
            Return (_X.ToString & ":"c & _Y.ToString & ":"c & _Z.ToString)
        End Function

    End Class

    Public Class LAB
        Public ReadOnly Min As Double = -128
        Public ReadOnly Max As Double = 127

        Sub New()
        End Sub

        Sub New(L As Double, A As Double, B As Double)
            Me.L = L
            Me.A = A
            Me.B = B
        End Sub

        Private _L As New Double
        Private _A As New Double
        Private _B As New Double

        Property L As Double
            Get
                Return _L
            End Get
            Set
                _L = LimitInRange(Value, 0, 100)
            End Set
        End Property

        Property A As Double
            Get
                Return _A
            End Get
            Set
                _A = LimitInRange(Value, Min, Max)
            End Set
        End Property

        Property B As Double
            Get
                Return _B
            End Get
            Set
                _B = LimitInRange(Value, Min, Max)
            End Set
        End Property

        Overrides Function ToString() As String
            Return (_L.ToString & ":"c & _A.ToString & ":"c & _B.ToString)
        End Function

    End Class

转换器

Public Function LABtoXYZ(LAB As LAB) As XYZ
        Dim X, Y, Z As New Double
        Y = ((LAB.L + 16.0) / 116.0)
        X = ((LAB.A / 500.0) + Y)
        Z = (Y - (LAB.B / 200.0))

        Dim Pow_X = Math.Pow(X, 3.0)
        Dim Pow_Y = Math.Pow(Y, 3.0)
        Dim Pow_Z = Math.Pow(Z, 3.0)

        Dim Less = (216 / 24389)

        If (Pow_X > Less) Then
            X = Pow_X
        Else
            X = ((X - (16.0 / 116.0)) / 7.787)
        End If
        If (Pow_Y > Less) Then
            Y = Pow_Y
        Else
            Y = ((Y - (16.0 / 116.0)) / 7.787)
        End If
        If (Pow_Z > Less) Then
            Z = Pow_Z
        Else
            Z = ((Z - (16.0 / 116.0)) / 7.787)
        End If

        Return New XYZ((X * 95.047), (Y * 100.0), (Z * 108.883))
    End Function

    Private Function XYZToRGB(XYZ As XYZ) As RGB
        Dim X, Y, Z As New Double
        Dim R, G, B As New Double
        Dim Pow As Double = (1.0 / 2.4)
        Dim Less As Double = 0.0031308

        X = (XYZ.X / 100)
        Y = (XYZ.Y / 100)
        Z = (XYZ.Z / 100)

        R = ((X * 3.24071) + (Y * -1.53726) + (Z * -0.498571))
        G = ((X * -0.969258) + (Y * 1.87599) + (Z * 0.0415557))
        B = ((X * 0.0556352) + (Y * -0.203996) + (Z * 1.05707))

        If (R > Less) Then
            R = ((1.055 * Math.Pow(R, Pow)) - 0.055)
        Else
            R *= 12.92
        End If
        If (G > Less) Then
            G = ((1.055 * Math.Pow(G, Pow)) - 0.055)
        Else
            G *= 12.92
        End If
        If (B > Less) Then
            B = ((1.055 * Math.Pow(B, Pow)) - 0.055)
        Else
            B *= 12.92
        End If

        Return New RGB(R, G, B)
    End Function

    Private Function RGBToXYZ(RGB As RGB) As XYZ
        Dim X, Y, Z As New Double
        Dim R, G, B As New Double
        Dim Less As Double = 0.04045

        R = (RGB.R / 255)
        G = (RGB.G / 255)
        B = (RGB.B / 255)

        If (R > Less) Then
            R = Math.Pow(((R + 0.055) / 1.055), 2.4)
        Else
            R = (R / 12.92)
        End If
        If (G > Less) Then
            G = Math.Pow(((G + 0.055) / 1.055), 2.4)
        Else
            G = (G / 12.92)
        End If
        If (B > Less) Then
            B = Math.Pow(((B + 0.055) / 1.055), 2.4)
        Else
            B = (B / 12.92)
        End If

        X = ((R * 0.4124) + (G * 0.3576) + (B * 0.1805))
        Y = ((R * 0.2126) + (G * 0.7152) + (B * 0.0722))
        Z = ((R * 0.0193) + (G * 0.1192) + (B * 0.9505))

        Return New XYZ(X * 100, Y * 100, Z * 100)
    End Function

    Private Function XYZToLAB(XYZ As XYZ) As LAB
        Dim X, Y, Z As New Double
        Dim L, A, B As New Double
        Dim Less As Double = 0.008856
        Dim Pow As Double = (1.0 / 3.0)

        X = ((XYZ.X / 100) / 0.9505)
        Y = (XYZ.Y / 100)
        Z = ((XYZ.Z / 100) / 1.089)

        If (X > Less) Then
            X = Math.Pow(X, Pow)
        Else
            X = ((7.787 * X) + (16.0 / 116.0))
        End If
        If (Y > Less) Then
            Y = Math.Pow(Y, Pow)
        Else
            Y = ((7.787 * Y) + (16.0 / 116.0))
        End If
        If (Z > Less) Then
            Z = Math.Pow(Z, Pow)
        Else
            Z = ((7.787 * Z) + (16.0 / 116.0))
        End If

        L = ((116.0 * Y) - 16.0)
        A = (500.0 * (X - Y))
        B = (200.0 * (Y - Z))

        'We solve the precision problem by rounding to nearest integer
        'This makes the conversion perfect.
        Return New LAB(CInt(L), CInt(A), CInt(B))
    End Function

在我将其标记为已解决之前需要进一步测试。

更新 5: 到目前为止还没有任何问题...当仅发布问题时,我不知道如何将其标记为已回答。 完整的免费代码和更多内容可以在here找到。

【问题讨论】:

  • 您没有发布应该包含一些逻辑来验证/转换输入值的相关类(LAB、XYZ 和 RGB) 舍入可能会导致一些不那么细微的差异。 XYZToRGB 中的颜色 (Return New Color(CSng(X), CSng(Y), CSng(Z))) 是什么?无论如何,打开 Option Explicit ON、Option Strict ON、Option Infer OFF,修复出现的问题(例如,RGBToXYZ 中的 Dim RGB = ColorToRGB(Color) 将不再(可能)被允许),然后查看转换。
  • 我建议您将 EasyRGB 代码与您尝试转换的原始 cginc 代码以及您的 LAB2XYZ 和 XYZ2LAB 代码进行比较。您应该会发现您的代码中有一些计算错误。
  • @Jimi 我用这些课程更新了问题。我忘了提到“Color”类是“Microsoft.Xna.Framework”中的颜色类,我不知道这是否会从常规的drawing.color中产生任何问题。 “Option Infer OFF”并没有起到太大作用,唯一中断的地方是它已经正确推断的地方。
  • Option Infer 不如 Option Strict 重要。后者,您必须打开,特别是当您执行此类操作(类型的多次转换)时。 Option Explicit ON 也很重要。这应该是您的默认设置。 VB.net 不是 JavaScript。我会尽快查看更新的代码。
  • @Jimi 默认情况下我有所有选项,除了推断。我目前正在尝试做 TnTinMn 建议的事情,到目前为止还没有运气。

标签: vb.net colors xna rgb cielab


【解决方案1】:

我没有解析你所有的代码,但是你的第一个代码块中的一个问题,在函数 RGBToXYZ

    X = (((X * 0.4124) + (Y * 0.3576) + (Z * 0.1805)) * 100.0)
    Y = (((X * 0.2126) + (Y * 0.7152) + (Z * 0.0722)) * 100.0)
    Z = (((X * 0.0193) + (Y * 0.1192) + (Z * 0.9505)) * 100.0)

    Return New XYZ(X, Y, Z)

你为 X 做矩阵,然后再次为 Y 的矩阵使用 X...但现在 X 是新值!这不是一个吝啬变量的地方。

这应该是这样的:

    Dim Xout, Yout, Zout As New Double

    Xout = ((X * 0.4124) + (Y * 0.3576) + (Z * 0.1805))
    Yout = ((X * 0.2126) + (Y * 0.7152) + (Z * 0.0722))
    Zout = ((X * 0.0193) + (Y * 0.1192) + (Z * 0.9505))

    Return New XYZ(Xout, Yout, Zout)

另外,我建议将 XYZ 保持在 0.0-1.0 范围内。

还有其他事情:

LABToXYZ 缺少所需的光源转换。它需要返回:

    X = (X * 0.95047)
    Z = (Z * 1.08883)

然后XYZtoLAB有:

    X = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
    Y = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
    Z = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))

这只是让 X Y 和 Z 都一样...

应该是(假设保持 XYZ 为 0-1):

    X = (XYZ.X / 0.95047)
    Y = (XYZ.Y)
    Z = (XYZ.Z / 1.08883)

我刚刚意识到您已经解决了自己的问题 — 我将把它留在这里,以防有人跑过它来寻找类似的答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-17
    • 2019-10-18
    • 2010-09-17
    • 1970-01-01
    • 2020-03-16
    • 2015-03-19
    • 1970-01-01
    • 2012-03-30
    相关资源
    最近更新 更多