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, но вышесказанное указывает на недостаток конвертации.
Вещи, которые я пробовал:
Этот код 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)
Я только что понял, что вы решили свой вопрос - я оставлю его здесь на случай, если кто-то наткнется на него в поисках похожих ответов.