我正在为项目创建自定义颜色选择器,它采用的是Photoshop风格,我可以使所有其他转换按预期方式工作,但我无法使RGBToLAB和LABToRGB正常工作。
问题不仅在于颜色显示不正确,而且转换也不完美。
样本:
初始LAB与最后一个LAB不同,这表明转换存在缺陷。我不仅会得到错误的颜色,而且值也会发生变化,尤其是当LAB.L假定为常数时(在此示例中,因为这是滑块当前所控制的)]
上面的LAB-> RGB-> LAB转换有缺陷,但是XYZ-> RGB-> XYZ转换也有缺陷。
显然,我对转换LABToLAB不感兴趣,但以上内容确实指出了转换中的缺陷。
我尝试过的事情:
This cginc code intended for unity, which is where i'm at now
Private Function LABToXYZ(LAB As LAB) As XYZ
Dim X, Y, Z As New Double
Y = ((LAB.L + 16.0) / 116.0)
X = ((LAB.A / 500.0) + Y)
Z = (Y - (LAB.B / 200.0))
Dim Less = 0.206897
If (X > Less) Then
X = Math.Pow(X, 3)
Else
X = ((X - 16.0 / 116.0) / 7.787)
End If
If (Y > Less) Then
Y = Math.Pow(Y, 3)
Else
Y = ((Y - 16.0 / 116.0) / 7.787)
End If
If (Z > Less) Then
Z = Math.Pow(Z, 3)
Else
Z = ((Z - 16.0 / 116.0) / 7.787)
End If
Return New XYZ(X, Y, Z)
End Function
Private Function XYZToRGB(XYZ As XYZ) As Color
Dim R, G, B As New Double
Dim X, Y, Z As New Double
X = (XYZ.X / 100)
Y = (XYZ.Y / 100)
Z = (XYZ.Z / 100)
R = ((X * 3.2406) + (Y * -1.5372) + (Z * -0.4986))
G = ((X * -0.9689) + (Y * 1.8758) + (Z * 0.0415))
B = ((X * 0.0557) + (Y * -0.204) + (Z * 1.057))
Dim Less As Double = 0.0031308
If (R > Less) Then
X = ((1.055 * Math.Pow(R, (1.0 / 2.4))) - 0.055)
Else
X = (R * 12.92)
End If
If (G > Less) Then
Y = ((1.055 * Math.Pow(G, (1.0 / 2.4))) - 0.055)
Else
Y = (G * 12.92)
End If
If (B > Less) Then
Z = ((1.055 * Math.Pow(B, (1.0 / 2.4))) - 0.055)
Else
Z = (B * 12.92)
End If
Return New Color(CSng(X), CSng(Y), CSng(Z))
End Function
Private Function RGBToXYZ(Color As Color) As XYZ
Dim RGB = ColorToRGB(Color)
Dim X, Y, Z As New Double
Dim Less As Double = 0.04045
If (RGB.R > Less) Then
X = Math.Pow(((RGB.R + 0.055) / 1.055), 2.4)
Else
X = (RGB.R / 12.92)
End If
If (RGB.G > Less) Then
Y = Math.Pow(((RGB.G + 0.055) / 1.055), 2.4)
Else
Y = (RGB.G / 12.92)
End If
If (RGB.B > Less) Then
Z = Math.Pow(((RGB.B + 0.055) / 1.055), 2.4)
Else
Z = (RGB.B / 12.92)
End If
X = (((X * 0.4124) + (Y * 0.3576) + (Z * 0.1805)) * 100.0)
Y = (((X * 0.2126) + (Y * 0.7152) + (Z * 0.0722)) * 100.0)
Z = (((X * 0.0193) + (Y * 0.1192) + (Z * 0.9505)) * 100.0)
Return New XYZ(X, Y, Z)
End Function
Private Function XYZToLAB(XYZ As XYZ) As LAB
Dim X, Y, Z As New Double
Dim L, A, B As New Double
Dim Less As Double = 0.008856
X = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
Y = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
Z = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
If (X > Less) Then
X = Math.Pow(X, (1.0 / 3.0))
Else
X = ((7.787 * X) + (16.0 / 116.0))
End If
If (Y > Less) Then
Y = Math.Pow(Y, (1.0 / 3.0))
Else
Y = ((7.787 * Y) + (16.0 / 116.0))
End If
If (Z > Less) Then
Z = Math.Pow(Z, (1.0 / 3.0))
Else
Z = ((7.787 * Z) + (16.0 / 116.0))
End If
L = ((116.0 * Y) - 16.0)
A = (500.0 * (X - Y))
B = (200.0 * (Y - Z))
Return New LAB(L, A, B)
End Function
Function ColorToRGB(Color As Color) As RGB
Return New RGB((Convert.ToInt32(Color.R) / 255), (Convert.ToInt32(Color.G) / 255), (Convert.ToInt32(Color.B) / 255))
End Function
Public Class RGB
Public ReadOnly Min As Double = 0
Public ReadOnly Max As Double = 1
Public Sub New()
End Sub
Public Sub New(R As Double, G As Double, B As Double)
Me.R = R
Me.G = G
Me.B = B
End Sub
Public Sub New(Color As Color)
Me.R = (Convert.ToInt32(Color.R) / 255)
Me.G = (Convert.ToInt32(Color.G) / 255)
Me.B = (Convert.ToInt32(Color.B) / 255)
End Sub
Private _R As New Double
Private _G As New Double
Private _B As New Double
Public Property R As Double
Get
Return _R
End Get
Set
_R = LimitInRange(Value, Min, Max)
End Set
End Property
Public Property G As Double
Get
Return _G
End Get
Set
_G = LimitInRange(Value, Min, Max)
End Set
End Property
Public Property B As Double
Get
Return _B
End Get
Set
_B = LimitInRange(Value, Min, Max)
End Set
End Property
Overrides Function ToString() As String
Return (_R.ToString & ":"c & _G.ToString & ":"c & _B.ToString)
End Function
End Class
Public Class XYZ
Public ReadOnly Min As Double = 0
Public ReadOnly Max As Double = 100
Public Sub New()
End Sub
Public Sub New(X As Double, Y As Double, Z As Double)
Me.X = X
Me.Y = Y
Me.Z = Z
End Sub
Private _X As New Double
Private _Y As New Double
Private _Z As New Double
Public Property X As Double
Get
Return _X
End Get
Set
_X = LimitInRange(Value, Min, Max)
End Set
End Property
Public Property Y As Double
Get
Return _Y
End Get
Set
_Y = LimitInRange(Value, Min, Max)
End Set
End Property
Public Property Z As Double
Get
Return _Z
End Get
Set
_Z = LimitInRange(Value, Min, Max)
End Set
End Property
Overrides Function ToString() As String
Return (_X.ToString & ":"c & _Y.ToString & ":"c & _Z.ToString)
End Function
End Class
Public Class LAB
Public ReadOnly Min As Double = -128
Public ReadOnly Max As Double = 127
Sub New()
End Sub
Sub New(L As Double, A As Double, B As Double)
Me.L = L
Me.A = A
Me.B = B
End Sub
Private _L As New Double
Private _A As New Double
Private _B As New Double
Property L As Double
Get
Return _L
End Get
Set
_L = LimitInRange(Value, 0, 100)
End Set
End Property
Property A As Double
Get
Return _A
End Get
Set
_A = LimitInRange(Value, Min, Max)
End Set
End Property
Property B As Double
Get
Return _B
End Get
Set
_B = LimitInRange(Value, Min, Max)
End Set
End Property
Overrides Function ToString() As String
Return (_L.ToString & ":"c & _A.ToString & ":"c & _B.ToString)
End Function
End Class
Function LimitInRange(Value As Double, Min As Double, Max As Double) As Double
Select Case Value
Case <= Min
Return Min
Case >= Max
Return Max
Case Else
Return Value
End Select
End Function
我需要VB.Net中的代码,这就是为什么我正在为我的项目转换和改编统一代码的原因,但是我遇到了麻烦,需要一些帮助。
[如果有人知道我在做什么错,我会很乐意听。
UPDATE 1:我试图通过不匹配两种转换方法来更正转换,但是我正接近完美的转换,但是我担心这么长时间解决这个问题可能会使我望而却步。
样本:
您看到的问题比以前少了,但仍然存在。
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
UPDATE 2:进一步的测试显示XNA.Framework.Color中异常异常的行为,导致任何分数都被解释为%。这意味着200.10将超过最大颜色值(255)的200%,这会将其限制为最大颜色值(255),因此,除非指定整数,否则最终可能会得到非常错误的输出。
我正在尝试使代码from this example as well不匹配。我觉得我正在进步,即使我不得不放弃在转换中使用XNA.Framework.Color类。
如果找到一个解决方案,我将提供最终解决方案。
UPDATE 3:在线测试here (source code here)和here显示我的LABToXYZ不正确。
我的结果:
他们的结果:
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会导致全0的XYZ,这是正确的行为,我无法判断出什么问题,是Z错误,但是我的代码中的错误在哪里?
更多示例here似乎表明我的代码是正确的,但是我仍然得到错误的Z。
UPDATE 4:进一步完善和重做所有代码,我发现对发现的示例here进行了转换和改编,给了我我想要的结果,即使示例中存在一些错误,尤其是^ 2.2,应该是^ 2.4。
我还发现了一些精度问题,必须将双精度数转换为整数才能使转换完美,但这可能是最终的更新,除非我遇到任何问题,否则我将在一段时间内继续解决这个问题在实践中测试代码。当我确信代码没有缺陷时,我会回来并将其标记为已回答。
样本:测试1
测试2
测试3
测试4
如上所示,有一个很小的变化,如果不进行四舍五入,将导致不完美的转换。
[The Classes
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
在将其标记为已解决之前,需要进行进一步测试。
UPDATE 5:到目前为止还没有任何问题...当仅发布问题时,我不知道如何将其标记为已回答。完整的免费代码和更多内容可以找到here。
我尚未解析您的所有代码,但在您的第一个代码块中,函数RGBToXYZ中存在问题。
,但是X现在是新值!这不是在变量上轻率的地方。X = (((X * 0.4124) + (Y * 0.3576) + (Z * 0.1805)) * 100.0) Y = (((X * 0.2126) + (Y * 0.7152) + (Z * 0.0722)) * 100.0) Z = (((X * 0.0193) + (Y * 0.1192) + (Z * 0.9505)) * 100.0) Return New XYZ(X, Y, Z)
您为X做矩阵,然后对Y矩阵使用X 再次
这应该是这样的:
Dim Xout, Yout, Zout As New Double Xout = ((X * 0.4124) + (Y * 0.3576) + (Z * 0.1805)) Yout = ((X * 0.2126) + (Y * 0.7152) + (Z * 0.0722)) Zout = ((X * 0.0193) + (Y * 0.1192) + (Z * 0.9505)) Return New XYZ(Xout, Yout, Zout)
此外,我建议将XYZ保持在0.0-1.0范围内。
LABToXYZ缺少所需的光源转换。它需要返回:
具有:X = (X * 0.95047) Z = (Z * 1.08883)
然后XYZtoLAB
X = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883)) Y = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883)) Z = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
哪个会使X Y和Z都相同...
应该(假设将XYZ保持为0-1):
X = (XYZ.X / 0.95047) Y = (XYZ.Y) Z = (XYZ.Z / 1.08883)
[我刚刚意识到您已经解决了自己的问题,我会把它留在这里,以防万一有人碰到它来寻找相似的答案。