LAB, RGB, XYZ Неверное преобразование цветов

Я делаю настраиваемую палитру цветов для проекта, она в стиле фотошопа, все остальные преобразования работают должным образом, но я не могу заставить 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.3836625496266
  • LAB _ 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 для моего проекта, однако я застрял и мне нужна помощь.

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

ОБНОВЛЕНИЕ 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.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 будет более 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,132216354033874
  • LAB _ 1: 0: 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: У меня не было никаких проблем до сих пор... Я не знаю, как пометить это как ответ, когда есть только опубликованный вопрос. Полный бесплатный код и многое другое можно найти здесь.

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, затем снова используете X для матрицы для Y... но теперь 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)

Я только что понял, что вы решили свой вопрос - я оставлю его здесь на случай, если кто-то наткнется на него в поисках похожих ответов.

Другие вопросы по тегам