LAB, RGB, XYZ Неверное преобразование цветов - PullRequest
0 голосов
/ 29 мая 2018

Я делаю пользовательскую палитру цветов для проекта, она в стиле фотошопа, все другие преобразования работают так, как и ожидалось, но я не могу заставить RGBToLAB и LABToRGB работать правильно.

Проблема не только в том, что цвета не представлены правильно, но и в том, что преобразование тоже не идеально.

Образец:

  • LAB _ 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,36636255: 66,3836425LAB _ 85.3739502460609: 0: 0

Исходный LAB не совпадает с последним LAB, это показывает, что преобразование имеет недостатки.Мало того, что я получаю неправильные цвета, но есть изменение в значениях, особенно когда LAB.L предполагается постоянным (в этом примере, потому что это то, что контролирует ползунок)

LAB-> RGB-> Преобразование LAB выше некорректно, но также и преобразование XYZ-> RGB-> XYZ.

Очевидно, я не заинтересован в преобразовании LABToLAB, но приведенное выше указывает на недостаток преобразования.

Вещи, которые я пробовал:

  1. Эта формула в Википедии

  2. Код EasyRGB

  3. Этот код javascript на github

  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, поэтому я работаю над преобразованием и адаптацией кода Unity для моего проекта, однакоЯ застрял и мне нужна помощь.

Если кто-нибудь знает, что я делаю неправильно, я буду рад выслушать.

UPDATE 1: Я пытался исправить ошибку больше, не совместив два метода конвертации, я все ближе к идеальной конверсии, но я боюсь, что, возможно, получил туннельное зрение, работая над этой проблемой длятак долго.

Образец:

  • LAB _ 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
  • LAB _ 2,29017121228677: -0,12373260790384: 0,26136297577 * 1074 1074видим, что проблема меньше, чем прежде, но она все еще существует.
        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 будет более 200% от максимального значения цвета (255), что ограничит его максимальным значением (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
      

    Но выполнение LAB со всеми 0результат XYZ со всеми 0, что является правильным поведением, я не могу сказать, что не так, это Z, это неправильно, но где ошибка в моем коде?

    Дополнительные примеры здесь кажетсяпредположить, что мой код правильный, но я все еще получаю неправильный Z.

    ОБНОВЛЕНИЕ 4: Дальнейшее уточнение и повторный повтор всего кода, я обнаружил, что преобразование иадаптация примеров ераунд здесь , дал мне результаты, которые я хотел, даже при том, что в этих примерах были некоторые ошибки, особенно ^ 2.2, когда это должно было быть ^ 2.4.

    Я также обнаружил некоторые проблемы с точностью, которые должны были превращать двойные числа в целые, чтобы преобразование было совершенным, но это может быть окончательное обновление, если только у меня не возникнет проблем, я оставлю этот вопрос открытым некоторое время, пока продолжупроверить код на практике.Я вернусь и отмечу ответ, когда я уверен, что код не содержит ошибок.

    Пример: тест 1

    • LAB _ 1: 0: 0
    • XYZ _ 0.105222895807779: 0,110706172533356: 0,120540201839494
    • RGB _ 4: 4: 4: 255
    • XYZ _ 0,115400959145268: 0,121410793419535: 0,1322 040 * 1: 11 1142 0404 * 1: 11 1142 040 * 1: 8 1142: 040 * 1: 11 1142 0404331: 1: 11: 11: 11: 0: 11: 0: 11: 11: 11: 11 0 11 11: 0

    Тест 2

    • LAB _ 10: 0: 0
    • XYZ _ 1.07024816003116: 1.12601992701628: 1.22604427713313
    • RGB _27: 27: 27: 255
    • XYZ _ 1.04175693531671: 1.09600940064882: 1.19355423730657
    • LAB _ 10: 0: 0

    Тест 3

    • LAB _ 100: 0: 0
    • XYZ _ 95.047: 100: 108.883
    • RGB _ 255: 255: 255: 255
    • XYZ _ 95.05: 100:108,9
    • LAB _ 100: 0: 0

    Тест 4

    • LAB _ 11: 0: 0
    • XYZ _ 1,19854884694432: 1.26100649883144: 1.37302170612264
    • RGB _ 29: 29: 29: 255
    • XYZ _ 1.16783071832485: 1.22864883569159: 1.33799858206814
    • LAB _ 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: У меня до сих пор не было проблем ... Я не знаю, как пометить это как ответ, когда есть только опубликованный вопрос.Полный бесплатный код и многое другое можно найти здесь .

...