赞
踩
RGB色彩模型:常用于电视、摄像机等显示器,遵循加色法则(光的三原色,越混越白);缺点是色彩空间不够均匀,不容易进行色差的评价;与直观的色调、饱和度、亮度没有直接联系,不符合人的认知心理
CMYK模型:用于彩色打印,对应青、品红、黄、黑,遵循减色法则(颜料三原色,越混越黑),可以满足打印的要求
YCbCr模型:是YUV压缩和偏移的版本,将亮度(Y)和色度(U, V)分开。Cb、Cr指蓝色色度、红色色度,图片(JPEG)、视频(MPEG)、DVD、摄像机、数字电视都采用这一格式,注意在不同色域(如BT.601, BT.709)下要使用相应的RGB2YUV转换矩阵
CIEXYZ色彩模型:为人眼实际感知的物体色彩响应值,是一种设备无关的颜色系统,可以由反射率、CIE XYZ人眼颜色匹配函数、光源SPD (Spectral Power Distribution)量化,是一种非均匀的颜色空间。各颜色空间各自独立,但可以通过XYZ作为媒介,变换至其他的颜色空间
CIELAB色彩模型:是一种设备无关的颜色系统,基于一种颜色不能同时既是蓝又是黄这个理论而建立。同一个椭圆内的颜色人眼观察起来是相同的,可见CIELAB的椭圆大小和形状优于CIELUV,是一种相对均匀的颜色空间
CIELCH色彩模型:采用了同CIELAB一样的颜色空间,但它采用L表示明度值,C表示饱和度以及H表示色调角度值的柱形坐标
HSL色彩模型 (Hue, Saturation, Lightness):类似于人眼对颜色的感知方式。HSL将RGB色彩模型中的点在圆柱坐标系中表示,H-色相,如红色、黄色等;S-饱和度,取0-100%;L-亮度,取0-100%
HSV色彩模型 (Hue, Saturation, Value):同样类似于人眼对颜色的感知方式。HSV在概念上常被认为是颜色的倒圆锥体,代表可区分出的饱和度和色相的级别数目随着明度接近黑色而降低。实际应用经常选择HSV色轮,色相表示为圆环, 三角形表示饱和度和明度,对饱和度的定义和HSL是明显不同的
CIE 1931 XYZ颜色空间:为人眼最大能感知色彩的范围,将人眼三刺激值量化后,取x和y平面图。其中黑线为普朗克曲线,从右至左的范围为1000K-20000K
包括:
def RGB2XYZ(RGB, mode):
if mode == "sRGB":
RGB2XYZ_matrix = np.array(
[[0.412391, 0.357584, 0.180481],
[0.212639, 0.715169, 0.072192],
[0.019331, 0.119195, 0.950532]])
if mode == "P3":
RGB2XYZ_matrix = np.array(
[[0.486571, 0.265668, 0.198217],
[0.228975, 0.691739, 0.079287],
[-0.0000003, 0.045113, 1.043944]])
XYZ = np.matmul(RGB2XYZ_matrix, RGB)
return XYZ
def XYZ2RGB(XYZ, mode):
if mode == "sRGB":
XYZ_to_RGB_matrix = np.array(
[[3.2404542, -1.5371385, -0.4985314],
[-0.9692660, 1.8760108, 0.0415560],
[0.0556434, -0.2040259, 1.0572252]])
elif mode == "P3":
XYZ_to_RGB_matrix = np.array(
[[2.4935223, -0.9313173, -0.4027241],
[-0.8294634, 1.7626022, 0.0236200],
[0.0358510, -0.0761817, 0.9570290]]
)
RGB = np.matmul(XYZ_to_RGB_matrix, XYZ)
return RGB
转换参考色Xr, Yr, Zr使用D65光源。其中,L∈(0, 100),a∈(-128, 127),代表红绿,b∈(-128, 127),代表黄蓝。在OpenCV中,对LAB数据对齐做了量化,使其处于0~255的范围。
def XYZ2LAB(XYZ): XYZ = np.array(XYZ).astype(np.float64) * 100 # d65 white = np.array([95.0489, 100.00, 108.884]) LAB = np.array([0.0, 0.0, 0.0]) fx = np.array([0.0, 0.0, 0.0]) for c in range(3): r_white = 0 if (XYZ[c] / white[c]) > ((6.0 / 29.0) ** 3.0): r_white = 1 fx[c] = fx[c] + r_white * (XYZ[c] / white[c]) ** (1 / 3.0) fx[c] = fx[c] + (1 - r_white) * ((841.0 / 108.0) * XYZ[c] / white[c] + 4.0 / 29.0) LAB[0] = 116 * fx[1] - 16 LAB[1] = 500 * (fx[0] - fx[1]) LAB[2] = 200 * (fx[1] - fx[2] return LAB
def LAB2XYZ(LAB): white = np.array([95.0489, 100.00, 108.884]) xyz = np.array([0.0, 0.0, 0.0]) XYZ = np.array([0.0, 0.0, 0.0]) fx = np.array([0.0, 0.0, 0.0]) fx[1] = (LAB[0] + 16) / 116 fx[0] = LAB[1] / 500 + fx[1] fx[2] = fx[1] - LAB[2] / 200 if fx[0] ** 3.0 > ((6.0 / 29.0) ** 3.0): xyz[0] = fx[0] ** 3.0 else: xyz[0] = (108.0 / 841.0) * fx[0] - 432.0 / 24389.0 if LAB[0] > 8.0: xyz[1] = ((LAB[0] + 16) / 116) ** 3.0 else: xyz[1] = LAB[0] * (3.0 / 29.0) ** 3.0 if fx[2] ** 3.0 > ((6.0 / 29.0) ** 3.0): xyz[2] = fx[2] ** 3.0 else: xyz[0] = (108.0 / 841.0) * fx[2] - 432.0 / 24389.0 for c in range(3): XYZ[c] = xyz[c] * white[c] / 100 return XYZ
def deltaC(lab1, lab2):
"""
求Delta E, Delta ab
:param lab1: [num,3]
:param lab2: [num,3]
:return: Delta E, Delta ab
"""
delta_E = np.power((np.sum(np.power((lab1[:, 0:] - lab2[:, 0:]), 2), axis=1)), 0.5)
delta_ab = np.power((np.sum(np.power((lab1[:, 1:] - lab2[:, 1:]), 2), axis=1)), 0.5) # shape=(120000,)
delta_E_avg = np.average(delta_E)
delta_ab_avg = np.average(deltaC)
return delta_E_avg, delta_ab_avg
def LAB2LCH(LAB)
LAB = np.array(LAB)
[L, A, B] = np.array([LAB[..., x] for x in range(LAB.shape[-1])])
C = np.hypot(A, B)
H = np.rad2deg(np.arctan2(B, A))
LCH = np.concatenate([x[..., np.newaxis]] for x in [L, C, H], axis=-1)
return LCH
def calculate_delta_hue_chroma(LCH1, LCH2):
[L1, C1, H1] = np.array([LCH1[..., x] for x in range(LCH1.shape[-1])])
[L2, C2, H2] = np.array([LCH2[..., x] for x in range(LCH2.shape[-1])])
D_hue = abs(H1 - H2)
D_chroma = C1 - C2
return D_hue, D_chroma
def RGB2HSL(RGB):
R, G, B = RGB[:, 0], RGB[:, 1], RGB[:, 2]
max_rgb, argmax_rgb = RGB.max(1)
min_rgb, argmin_rgb = RGB.min(1)
del_rgb = max_rgb - min_rgb
del_R = 60.0 * (G - B) / (del_rgb + epsilon)
del_G = 60.0 * (B - R) / (del_rgb + epsilon) + 120.0
del_B = 60.0 * (R - G) / (del_rgb + epsilon) + 240.0
H = torch.stack((del_R, del_G, del_B), dim=0).gather(dim=0, index=argmax_rgb.unsqueeze(0)).squeeze(0)
H = torch.where(H < 0, H + 360, H)
L = (max_rgb + min_rgb) / 2
S = torch.where(L > 0.5, del_rgb / (2 - 2 * L + epsilon), del_rgb / (2 * L + epsilon))
HSL = torch.stack((H, S, L), dim=1)
return HSL
def HSL2RGB(HSL): H, S, L = HSL[:, 0].clone(), HSL[:, 1], HSL[:, 2] var_2 = torch.where(L < 0.5, L * (1 + S), (L + S) - (L * S)) var_1 = 2.0 * L - var_2 H = H / 360.0 R = hue2rgb(var_1, var_2, h + (1.0 / 3.0)) G = hue2rgb(var_1, var_2, h) B = hue2rgb(var_1, var_2, h - (1.0 / 3.0)) rgb_lut = torch.stack((R, G, B), dim=1) return rgb_lut def hue2rgb(v1, v2, vh): vh_temp = (vh + 1).frac() c1 = v1 + (v2 - v1) * 6.0 * vh_temp c2 = c3 = v2 c4 = v1 + (v2 - v1) * ((2.0 / 3.0) - vh_temp) * 6.0 c5 = c6 = v1 c = torch.stack((c1, c2, c3, c4, c5, c6), dim=0) index = (torch.ceil(vh_temp * 6)).to(torch.long) - 1 index = torch.where(index < 0, 0, index) c = (c.gather(dim=0, index=index.unsqueeze(0))).squeeze(0) return c
# answer def rgb2hsv(img): _img = img.copy().astype(np.float32)# / 255 v_max = _img.max(axis=2) v_min = _img.min(axis=2) v_argmin = _img.argmin(axis=2) hsv = np.zeros_like(_img, dtype=np.float32) r, g, b = np.split(_img, 3, axis=2) r, g, b = r[..., 0], g[..., 0], b[..., 0] diff = np.maximum(v_max - v_min, 1e-10) # Hue ind = v_argmin == 2 hsv[..., 0][ind] = 60 * (g - r)[ind] / diff[ind] + 60 ind = v_argmin == 0 hsv[..., 0][ind] = 60 * (b - g)[ind] / diff[ind] + 180 ind = v_argmin == 1 hsv[..., 0][ind] = 60 * (r - b)[ind] / diff[ind] + 300 ind = v_max == v_min hsv[..., 0][ind] = 0 # Saturation hsv[..., 1] = v_max - v_min # Value hsv[..., 2] = v_max return hsv
def hsv2rgb(hsv): h, s, v = np.split(hsv, 3, axis=2) h, s, v = h[..., 0], s[..., 0], v[..., 0] _h = h / 60 x = s * (1 - np.abs(_h % 2 - 1)) z = np.zeros_like(x) vals = np.array([[s, x, z], [x, s, z], [z, s, x], [z, x, s], [x, z, s], [s, z, x]]) img = np.zeros_like(hsv) for i in range(6): ind = _h.astype(int) == i for j in range(3): img[..., j][ind] = (v - s)[ind] + vals[i, j][ind] return np.clip(img, 0, 255).astype(np.uint8)
思路:由Kr, Kg, Kb而来,代表整体R, G, B亮度比例
def RGB2YUV(RGB, mode): if mode == "BT601": RGB_to_YUV_matrix = np.array( [[0.299, 0.587, 0.114], [-0.169, -0.331, 0.5], [0.5, -0.419, -0.081]] ) elif mode == "BT709": RGB_to_YUV_matrix = np.array( [[0.2126, 0.7152, 0.072], [-0.1146, -0.3854, 0.5], [0.5, -0.4542, -0.0458]] ) elif mode == "AdobeRGB": RGB_to_YUV_matrix = np.array( [[0.2973, 0.6274, 0.0753], [-0.1608, -0.3392, 0.5], [0.5, -0.4464, -0.0536]] ) elif mode == "DisplayP3": RGB_to_YUV_matrix = np.array( [[0.2290, 0.6917, 0.0793], [-0.1243, -0.3757, 0.5], [0.5, -0.4486, -0.0514]] ) elif mode == "TheaterP3": RGB_to_YUV_matrix = np.array( [[0.2095, 0.7216, 0.0689], [-0.1125, -0.3875, 0.5], [0.5, -0.4564, -0.0436]] ) elif mode == "BT2020": RGB_to_YUV_matrix = np.array( [[0.2627, 0.6780, 0.0593], [-0.1396, -0.3604, 0.5], [0.5, -0.4598, -0.0402]] ) YUV = np.matmul(RGB_to_YUV_matrix, RGB) return YUV
def YUV2RGB(YUV, mode): if mode == "BT601": YUV_to_RGB_matrix = np.array( [[1.0000, -0.0009, 1.4017], [1.0000, -0.3437, -0.7142], [1.0000, 1.7722, 0.0010]] ) elif mode == "BT709": YUV_to_RGB_matrix = np.array( [[1.0000, -0.0009, 1.5747], [1.0000, -0.1873, -0.4682], [1.0000, 1.8556, 0.0000]] ) elif mode == "AdobeRGB": YUV_to_RGB_matrix = np.array( [[1.0000, 0.0000, 1.4053], [1.0000, -0.2220, -0.6661], [1.0000, 1.8494, 0.0000]] ) elif mode == "DisplayP3": YUV_to_RGB_matrix = np.array( [[1.0000, 0.0000, 1.5421], [1.0000, -0.2111, -0.5104], [1.0000, 1.8414, 0.0000]] ) elif mode == "TheaterP3": YUV_to_RGB_matrix = np.array( [[1.0000, 0.0000, 1.5810], [1.0000, -0.1778, -0.4590], [1.0000, 1.8622, 0.0000]] ) elif mode == "BT2020": YUV_to_RGB_matrix = np.array( [[1.0000, 0.0000, 1.4746], [1.0000, -0.1646, -0.5714], [1.0000, 1.8814, 0.0000]] ) RGB = np.matmul(YUV_to_RGB_matrix, YUV) return RGB
所有RGB和YCbCr的范围都是0~255,与RGB2YUV转换公式一致,最后加上0, 128, 128用来拉伸至255
指Y, Cb, Cr的值不到最大值的255,其中Y的范围16~235,Cb和Cr为16-240,RGB范围为0-255。Limit Range由Full Range转换而来,先将Y, U, V各通道乘以219, 224, 224转成Y, Cb, Cr,再将得到的数除以255得到系数,如果要转为8bit再乘以256
即先做RGB2XYZ, 再做XYZ2LAB
def rgb2lab(rgb, color_gamut): """ RGB2LAB :param rgb: rgb矩阵 :param color_gamut: 色域 :return: lab矩阵 """ # ============sRGB转xyz============ xyz = rgb # 转为float # gamma矫正 mask = xyz > 0.04045 xyz[mask] = np.power((xyz[mask] + 0.055) / 1.055, 2.4) xyz[~mask] /= 12.92 # 线性变换 if color_gamut == 'sRGB': xyz = xyz @ MAT_RGB2XYZ.T if color_gamut == 'P3': xyz = xyz @ MAT_P32XYZ.T # ============xyz转lab============ XYZ_REF_WHITE = np.array([0.95047, 1.0, 1.08883]) # 白点校正 xyz /= XYZ_REF_WHITE # nonlinear transform mask = xyz > 0.008856 xyz[mask] = np.power(xyz[mask], 1.0 / 3.0) xyz[~mask] = 7.787 * xyz[~mask] + 16.0 / 116.0 x, y, z = xyz[..., 0], xyz[..., 1], xyz[..., 2] # linear transform lab = np.zeros(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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。