# RGB与Lab转换

18 篇文章 9 订阅
10 篇文章 3 订阅
1 篇文章 0 订阅

## 公式

sRGB不能直接转换为Lab，需要用XYZ过渡：sRGB -> XYZ -> Lab

### sRGB转Lab

#### sRGB转XYZ

1. sRGB通过gamma变换转为RGB
2. RGB通过线性映射转为XYZ

sRGB通过gamma变换转为RGB

R = r / 255 G = g / 255 B = b / 255 R = r / 255 \\[2ex] G = g / 255 \\[2ex] B = b / 255

gamma变换（t 代表R, G, B）：
t = { ( t + 0.055 1.055 ) 2.4 , i f : t > 0.04045 t 12.92 , e l s e t= \begin{cases} \big(\frac{t+ 0.055}{1.055}\big)^{2.4}, if: t>0.04045 \\[2ex] \frac {t} {12.92}, else \end{cases}

X = 0.412453 ⋅ R + 0.357580 ⋅ G + 0.180423 ⋅ B Y = 0.212671 ⋅ R + 0.715160 ⋅ G + 0.072169 ⋅ B Z = 0.019334 ⋅ R + 0.119193 ⋅ G + 0.950227 ⋅ B X = 0.412453 \cdot R + 0.357580 \cdot G + 0.180423 \cdot B \\[2ex] Y = 0.212671 \cdot R + 0.715160 \cdot G + 0.072169 \cdot B \\[2ex] Z = 0.019334 \cdot R + 0.119193 \cdot G + 0.950227 \cdot B

M R G B 2 X Y Z = [ 0.412453 0.357580 0.180423 0.212671 0.715160 0.072169 0.019334 0.119193 0.950227 ] M_{RGB2XYZ} = \begin{bmatrix} 0.412453 & 0.357580 & 0.180423 \\[2ex] 0.212671 & 0.715160 & 0.072169 \\[2ex] 0.019334 & 0.119193 & 0.950227 \end{bmatrix}

[ X Y Z ] = [ R G B ] M R G B 2 X Y Z T \begin{bmatrix} X & Y & Z \end{bmatrix} = \begin{bmatrix} R & G & B \end{bmatrix} M_{RGB2XYZ}^T

Understanding CIE Illuminants and Observers

#### XYZ转Lab

x = X / X r e f _ w h i t e y = Y / Y r e f _ w h i t e z = Z / Z r e f _ w h i t e x = X / X_{ref\_white} \\[2ex] y = Y / Y_{ref\_white} \\[2ex] z = Z / Z_{ref\_white} \\[2ex]

t = { t 1 / 3 , i f : t > ( 6 29 ) 3 ( 1 3 ) ( 29 6 ) 2 ⋅ t + 16 116 , e l s e t = \begin{cases} t^{1/3}, if: t> (\frac {6}{29})^3 \\[2ex] (\frac {1}{3})(\frac {29}{6})^2 \cdot t + \frac {16}{116}, else \end{cases}

（另外要喷一下，这个分段公式在交界处函数值不连续，也不知道是根据什么道理设计出来的）

L = 116 ⋅ y − 16 a = 500 ⋅ ( x − y ) b = 200 ⋅ ( y − z ) L = 116 \cdot y - 16 \\[2ex] a = 500 \cdot (x - y) \\[2ex] b = 200 \cdot (y - z)

### Lab转sRGB

#### Lab转XYZ

y = ( L + 16 ) / 116 x = a / 500 + y z = y − b / 200 y = (L + 16) / 116 \\[2ex] x = a / 500 + y \\[2ex] z = y - b / 200

t = { t 3 , i f : t > 6 / 29 ( t − 16 116 ) ⋅ 3 ⋅ ( 6 29 ) 2 , e l s e t = \begin{cases} t^3, if: t > 6/29 \\[2ex] (t- \frac {16}{116}) \cdot 3 \cdot (\frac {6}{29})^2, else \end{cases}

X = x ⋅ X r e f _ w h i t e Y = y ⋅ Y r e f _ w h i t e Z = z ⋅ Z r e f _ w h i t e X = x \cdot X_{ref\_white} \\[2ex] Y = y \cdot Y_{ref\_white} \\[2ex] Z = z \cdot Z_{ref\_white} \\[2ex]

#### XYZ转sRGB

[ R G B ] = [ X Y Z ] ( M R G B 2 X Y Z T ) − 1 \begin{bmatrix} R & G & B \end{bmatrix} = \begin{bmatrix} X & Y & Z \end{bmatrix} (M_{RGB2XYZ}^T)^{-1}

gamma变换（t 代表R, G, B）：
t = { 1.055 ⋅ t 1 / 2.4 − 0.055 , i f : t > 0.0031308 12.92 ⋅ t , e l s e t = \begin{cases} 1.055 \cdot t^{1/2.4} - 0.055, if: t > 0.0031308 \\[2ex] 12.92 \cdot t, else \end{cases}

t = { 1 , i f : t > 1 0 , i f : t < 0 t , e l s e t = \begin{cases} 1, if: t >1 \\[2ex] 0, if: t<0 \\[2ex] t, else \end{cases}

r = R ⋅ 255 g = G ⋅ 255 b = B ⋅ 255 r = R \cdot 255 \\[2ex] g = G \cdot 255 \\[2ex] b = B \cdot 255

## 程序

# -*- coding: utf-8 -*-
import numpy as np
from skimage import color

MAT_RGB2XYZ = np.array([[0.412453, 0.357580, 0.180423],
[0.212671, 0.715160, 0.072169],
[0.019334, 0.119193, 0.950227]])

MAT_XYZ2RGB = np.linalg.inv(MAT_RGB2XYZ)

XYZ_REF_WHITE = np.array([0.95047, 1.0, 1.08883])

def rgb_to_lab(rgb):
"""
Convert color space from rgb to lab

Parameters:
-----------
rgb: numpy array, dtype = uint8
3-dim array, shape is [H, W, C], C must be 3

Returns:
--------
numpy array in lab color space, dtype = float
"""
return xyz_to_lab(rgb_to_xyz(rgb))

def lab_to_rgb(lab):
"""
Convert color space from lab to rgb

Parameters:
-----------
lab: numpy array, dtype = float
3-dim array, shape is [H, W, C], C must be 3

Returns:
--------
numpy array in rgb color space, dtype = uint8
"""
return xyz_to_rgb(lab_to_xyz(lab))

def rgb_to_xyz(rgb):
"""
Convert color space from rgb to xyz

Parameters:
-----------
rgb: numpy array, dtype = uint8
3-dim array, shape is [H, W, C], C must be 3

Returns:
--------
xyz: numpy array, dtype = float
array in xyz color space
"""
# convert dtype from uint8 to float
xyz = rgb.astype(np.float64) / 255.0

# gamma correction

# linear transform
xyz = xyz @ MAT_RGB2XYZ.T
return xyz

def xyz_to_rgb(xyz):
"""
Convert color space from xyz to rgb

Parameters:
-----------
xyz: numpy array, dtype = float
3-dim array, shape is [H, W, C], C must be 3

Returns:
--------
rgb: numpy array, dtype = uint8
array in rgb color space
"""
# linear transform
rgb = xyz @ MAT_XYZ2RGB.T

# gamma correction

# clip and convert dtype from float to uint8
rgb = np.round(255.0 * np.clip(rgb, 0, 1)).astype(np.uint8)
return rgb

def xyz_to_lab(xyz):
"""
Convert color space from xyz to lab

Parameters:
-----------
xyz: numpy array, dtype = float
3-dim array, shape is [H, W, C], C must be 3

Returns:
--------
lab: numpy array, dtype = float
array in lab color space
"""
# normalization
xyz /= XYZ_REF_WHITE

# nonlinear transform
x, y, z = xyz[..., 0], xyz[..., 1], xyz[..., 2]

# linear transform
lab = np.empty(xyz.shape)
lab[..., 0] = (116.0 * y) - 16.0  # L channel
lab[..., 1] = 500.0 * (x - y)  # a channel
lab[..., 2] = 200.0 * (y - z)  # b channel
return lab

def lab_to_xyz(lab):
"""
Convert color space from lab to xyz

Parameters:
-----------
lab: numpy array, dtype = float
3-dim array, shape is [H, W, C], C must be 3

Returns:
--------
xyz: numpy array, dtype = float
array in xyz color space
"""
# linear transform
l, a, b = lab[..., 0], lab[..., 1], lab[..., 2]
xyz = np.empty(lab.shape)
xyz[..., 1] = (l + 16.0) / 116.0
xyz[..., 0] = a / 500.0 + xyz[..., 1]
xyz[..., 2] = xyz[..., 1] - b / 200.0
index = xyz[..., 2] < 0
xyz[index, 2] = 0

# nonlinear transform

# de-normalization
xyz *= XYZ_REF_WHITE
return xyz

if __name__ == '__main__':
rgb = np.array([[[150, 150, 0]]], dtype=np.uint8)
xyz = rgb_to_xyz(rgb)
lab = xyz_to_lab(xyz)
xyz_ = lab_to_xyz(lab)
rgb_ = xyz_to_rgb(xyz_)

print('-' * 15, ' self defined function result ', '-' * 15)
print('rgb:', rgb)
print('xyz:', xyz)
print('lab:', lab)
print('xyz_inverse:', xyz_)
print('rgb_inverse:', rgb_)

xyz2 = color.rgb2xyz(rgb)
lab2 = color.xyz2lab(xyz2)
xyz2_ = color.lab2xyz(lab2)
rgb2_ = color.xyz2rgb(xyz2_)
rgb2_ = np.round(255.0 * np.clip(rgb2_, 0, 1)).astype(np.uint8)

print('-' * 15, ' skimage result ', '-' * 15)
print('rgb:', rgb)
print('xyz:', xyz2)
print('lab:', lab2)
print('xyz_inverse:', xyz2_)
print('rgb_inverse:', rgb2_)



## RGB转Lab后的数值范围

Lab数据类型的取值范围被定义为：
L：[0, 100]
a：[-128, 127]
b：[-128, 127]

# -*- coding: utf-8 -*-
import numpy as np
from skimage import color

if __name__ == '__main__':
rgb = np.zeros([1, 256 * 256 * 256, 3])
index = 0
for r in range(0, 256):
print('\rr = %d' % r, end='')
for g in range(0, 256):
for b in range(0, 256):
rgb[0, index, :] = np.array([r, g, b])
index += 1
print()
rgb = np.uint8(rgb)
lab = color.rgb2lab(rgb)

print('L, min: %f, max: %f' % (np.min(lab[0, :, 0]), np.max(lab[0, :, 0])))
print('a, min: %f, max: %f' % (np.min(lab[0, :, 1]), np.max(lab[0, :, 1])))
print('b, min: %f, max: %f' % (np.min(lab[0, :, 2]), np.max(lab[0, :, 2])))


L, min: 0.000000, max: 100.000000
a, min: -86.183030, max: 98.233054
b, min: -107.857300, max: 94.478122


L = L / 100.0 a = ( a + 86.183030 ) / 184.416084 b = ( b + 107.857300 ) / 202.335422 L = L / 100.0 \\[2ex] a = (a + 86.183030) / 184.416084 \\[2ex] b = (b + 107.857300) / 202.335422

• 18
点赞
• 92
收藏
觉得还不错? 一键收藏
• 11
评论
11-27 1724
07-12
05-26 1391
01-17 1377
07-09
11-07 1847
10-29 8644
01-04
11-24
08-24
12-16
10-28 1万+
12-25 779

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、付费专栏及课程。