我正在为一个项目制作一个自定义颜色选择器,它采用 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 不感兴趣,但上面确实指出了转换中的一个缺陷。
我尝试过的事情:
-
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:到目前为止还没有任何问题......当只发布问题时,我不知道如何将其标记为已回答。完整的免费代码和更多可以在这里找到。