1

我正在为一个项目制作一个自定义颜色选择器,它采用 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. 维基百科上的这个公式

  2. EasyRGB 的代码

  3. github上的这个javascript代码

  4. 这个 cginc 代码用于统一,这就是我现在的位置

       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),因此除非您指定整数,否则最终可能会得到非常错误的输出。

我也试图与此示例中的代码不匹配。我觉得我在进步,即使我不得不放弃在转换中使用 XNA.Framework.Color 类。

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

更新 3:此处的 在线测试(此处的 源代码)此处显示我的 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,但我的代码中的错误在哪里?

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

更新 4: 进一步完善和重做所有代码,我发现对此处找到的示例进行转换和改编,给了我想要的结果,即使这些示例中有一些错误,值得注意的是 ^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:到目前为止还没有任何问题......当只发布问题时,我不知道如何将其标记为已回答。完整的免费代码和更多可以在这里找到。

4

1 回答 1

0

我没有解析你所有的代码,但是你的第一个代码块中的一个问题,在函数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 做矩阵,然后再用 XY 的矩阵……但现在 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))

这只是让 XY 和 Z 都一样......

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

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

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

于 2020-01-06T13:16:17.847 回答