前言:色彩客观评测是指通过分析色卡在指定光源环境下的色彩客观数据来判断当前相机是否满足客观标准,本文整理了Imatest中ColorCheck测试步骤及色彩相关指标的数据解读,并在第五章中介绍了一种批量色卡识别并输出结果报表的解决方案。

一、可测图像质量指标

通常在可控光源环境(灯箱/实验室/实景)里拍摄色卡可用于测试相机如下图像质量指标:

  1. 色彩。包括color accuracy(色精度)、white balance(白平衡)、saturation(饱和度)等。
  2. 噪声。包括noise(噪声)、SNR(信噪比)、visual noise(视觉噪声)等。
  3. Tonal Response, Dynamic Range, Gamma等。

二、Imatest色卡测试及操作流程

请添加图片描述
(1)手机/相机正对色卡拍摄,并保证色卡占预览面积的70%左右,光源包括D65、TL84、CWF、A光等;
(2)导出图片到Imatest Master工具中,点击"Colorcheck",请添加图片描述
(3)依次选择①→⑦,
请添加图片描述
上图中,
①——选择被拍摄色卡的基准参考,默认X-rite default,具体看所购买的色卡注明。

②——色彩空间,根据被测相机/手机的色彩空间而定,近几年上市的主流旗舰手机如iphone 13系列均采用的P3色域。

③——噪声fig展示,可按需选择需要展示的噪声指标如SNR等。

④——偏色误差展示,可按需选择CIE1976, CIE1994, CIE2000等标准下的△C, △E。CIE1976, CIE1994, CIE2000均为CIE(国际照明委员会)定义的色彩空间,他们计算偏色误差的标准不同,CIE1976是直接计算的理想落点与实际落点的欧氏距离,致使△E1976数值偏大,但在高饱和场景时△E的计算结果就不会太准确;而CIE1994和CIE2000在计算时就会考虑到人眼在高饱和色彩时减弱敏感度的真实感受,相对而言CIE2000更精准。

⑤——容许椭圆展示,可按需在a*b* -color error图上选择对应容许椭圆进行展示(结果如Fig 3.3(a)所示)。
请添加图片描述
关于容许椭圆,需要知道SDCM(色彩匹配标准差)反映的是色彩空间中两个颜色点之间的色差,麦克亚当椭圆(MacAdam ellipses)定义了色彩空间中两个点之间的色差标准(一般可称为容许椭圆),以24色卡为例,当色块实际落点位于理想落点所在的容许椭圆外,表明实际落点超出了容许范围,客观上意味着当前色块色差偏离大;反之亦然。类似地Delta-C 94 ellipses,Delta-C 2000 ellipses都是类似解读。

实际上,各手机/相机厂商/第三方检测机构(Imatest/IE/DXO等)都可以针对各色块定义自己的容许椭圆,椭圆中心以色卡各色块理想落点为中心,椭圆长短轴则根据厂商需求去自定义,理论上椭圆半径越小说明对色差的容许越苛刻,在有效的管控下出来的色彩就会越接近想要的理想落点。

⑥——ROI选取(针对下一次运行时而言)。官方推荐是选2或3或4,三者的区别就是,2是一个半自动的粗略ROI提取,会展示各个色块的提取框如下;3是全自动ROI检测,但会提供展示及确认的步骤;4就是全自动提取并跳过ROI确认。实测2,3,4最后都会需要确认,个人感觉2或3还是会稳妥些。如下Fig 2.5为colorchecker在提取到色块后的confirm过程。
请添加图片描述
⑦——按需plot相应的图表。

三、Imatest输出数据解读

3.1 图像色彩相关指标

请添加图片描述

(1)色彩精度(Color Accuracy)

色彩精度反映的是手机/相机对色彩的还原能力,用Imatest工具可以测得色卡各色块的实际落点,某一色块实际落点与理想落点之间的差异大小即代表了该色块的色精度水平。通常用△E(色差)或△C(彩度差)来衡量,以Lab色彩空间为例,△E的值取决于L,a,b三个通道共同作用;△C则主要跟a,b两个通道的值相关(即不考虑亮度的影响故而也称彩差)。如Fig 3.1所示,通常测色卡前18个色块的色彩精度作参考。

(2)白平衡误差(WB Error)

白平衡误差反映的手机/相机对白色被摄物的颜色还原能力,如Fig 3.1所示,通常测色卡最后一行19-24色块的WB Error作参考。

(3)色彩饱和度(Saturation)

即色彩的鲜艳程度,包括各色块的Chroma值及1-18色块的平均饱和度。

3.2 输出报表解读

如下主要截取色彩相关指标进行解读。

(1)RGB, Lab, xyY, Chroma值

Fig 3.2(a)

Fig 3.2(a)

上图中, meas代表measured,即实测结果;ideal即理想结果。

①——表示各色块测得的平均RGB值(归一化后),各色块被统计的像素源于Fig 2.5中各个青色方框所框选到的像素:

[R, G, B] = np.divide(RGB, 255.0)

②——表示转换到Lab空间下的色彩表示(sRGB→XYZ→Lab),公式如下(代码实现见附录[【A1】色彩空间转换](#【A1】色彩空间转换(RGB→XYZ, XYZ→Lab, XYZ→xyY))):
请添加图片描述
请添加图片描述

③——表示转换到xyY空间下的色彩表示(sRGB→XYZ→xyY),XYZ→xyY公式如下:
请添加图片描述

④——各色块饱和度Chroma计算公式如下:
请添加图片描述

⑤——为色卡出厂时各色块的参考(理想)RGB值。

(2)Delta-E, Delta-C

请添加图片描述

Fig 3.2(b)

Delta-E*ab和Delta-C默认是采用的CIE-1976标准,公式如下(代码实现见附录【A2】):

请添加图片描述

Delta-E*94和Delta-C94采用的是CIE-1994标准,公式如下(代码实现见附录[【A2】Delta-E&Delta-C](#【A2】Delta-E, Delta-C(CIE1976, CIE1994, CIE2000, CMC))):

请添加图片描述

Delta-E*00和Delta-C00采用的是CIE-2000标准,公式如下:

请添加图片描述

(3)White Balance Error

请添加图片描述

Fig 3.2(c)

WB ERR S列所代表的就是HSV色彩模型下19-24色块对应的S值作白平衡误差,计算公式如下:
S = M a x ( R , G , B ) − M i n ( R , G , B ) M a x ( R , G , B ) S = \frac{Max(R,G,B)-Min(R,G,B)}{Max(R,G,B)} S=Max(R,G,B)Max(R,G,B)Min(R,G,B)

(4)Mean camera Chroma

Fig 3.2(d)

Fig 3.2(d)

Mean camera chroma%单元格对应的就是平均饱和度,计算公式如下(代码实现见附录【A3】):
M e a n   C h r o m a = 100 % × m e a n ( a i _ m e a s 2 + b i _ m e a s 2 ) m e a n ( a i _ m e a s 2 + b i _ m e a s 2 ) Mean\ Chroma =100\% \times \frac{mean(\sqrt{a^2_{i\_meas}+b^2_{i\_meas}})}{mean(\sqrt{a^2_{i\_meas}+b^2_{i\_meas}})} Mean Chroma=100%×mean(ai_meas2+bi_meas2 )mean(ai_meas2+bi_meas2 )

3.3 图表解读

(1)a*b*图

请添加图片描述

Fig 3.3(a)

上图说明了设备无关的CIELAB色彩空间的a*b*平面上的颜色误差,其中a*是横轴(绿-红),b*是纵轴(蓝-黄)。

  • 方块是各色块的理想落点(a*, b*)值,该数值为所选色卡出厂的标准,在[3.2(1)](#(1)RGB, Lab, xyY, Chroma值)也有提及;
  • 圆圈是各色块实测落点的(a*, b*)值。正方形或圆形附近的数字对应于色卡色块的编号;
  • 色块 19-24(底行)的数字被省略,因为它们的 (a*, b*) 值聚集在 (0, 0) 周围;
  • 理想落点与实测落点之间以线段相连,映射在a*b*平面坐标上代表的就是二者之间的彩度差即△C;
  • 各理想落点外围均绘制有MacAdam椭圆或类椭圆,椭圆以理想落点为中心,代表了当前标准下各色块的色差容许范围。如图所示,色块5的实测落点位于椭圆内则代表色差可接受;色块10的实测落点位于椭圆外则代表当前色差过大超出容许范围。

(2)标准色块对照图

请添加图片描述

Fig 3.3(b)

Fig 3.3(b)图中上半部分,每个色块内部都嵌套了2个小矩形块,如下图
请添加图片描述

Fig 3.3(c)

其含义如下:

  • 最外围矩形1是实拍的patch,对应于Fig 3.3(a)-a*b*图中的实测落点;
  • 中心矩形2是实拍patch的参考值,该值是对实拍patch的亮度做了校正,校正值是灰块做二阶拟合后的结果(todo:这里具体是怎么矫正的我还没整明白,请教了朋友说亮度是按照反射率*cmf算出来的),默认情况下以HSL颜色完成,但也可以在HSL, HSV, xyY, La*b*之间选择。矩形1和矩形2的平均亮度接近,矩形2可能在某些色块中更暗,而在其他色块中更亮。矩形2和矩形3对应于Fig 3.3(a)中的小方块即理想落点。
  • 矩形3是当前色块的理想值,无需亮度校正。对于所有色块,矩形2的亮度将始终比矩形3亮或暗,具体取决于曝光。

请添加图片描述

Fig 3.3(d)

图Fig 3.3(d)是Fig 3.3(b)图中的下半部分,反映了当前色卡图像的白平衡误差,该误差以4种方式量化如下,

  1. △C2000。CIE2000标准下的偏色误差指标,最接近人眼感知。
  2. HSV色彩模型中的饱和度S。S的值介于0(表示完美的中性灰)到1(表示完全饱和的颜色)之间。白平衡误差往往在灰块上表现最为明显(对应底行为2-5色块)。对于 S < 0.02,几乎看不见。对于 S > 0.10 来说,这是相当严重的WB误差。图像质量管控中通常会参考这一数值来评判当前相机的白平衡表现
  3. CCT(相关色温)误差,单位为开尔文。以K为单位的色温通常用于指定照明环境,比如D65(6500K),D50(5000K)。CCT误差反映的是照明色温的变化,通常较高的色温呈现为更多的蓝色,感知上会“更冷”;较低色温呈现为黄色,感知上会“更暖”。
  4. 以Mireds为单位的反色温(微倒数度)(蓝色),其中 Mireds = 106/(开尔文度)。比 CCT 误差在感知上更统一的指标。它用于指定光学色彩校正滤光片的强度,但在数字时代使用不多,因为很少需要这种滤光片。

四、色彩客观管控标准

在图像质量管控中,客观国标测试是检验当前产品是否达到国家标准合格上市的重要一环。国内终端厂商主要在《YD/T1607-2016 移动终端图像及视频传输特性技术要求和测试方法》的基础上制定自家的客观质量标准,并基于此进行客观图像质量管控。

在该国标中关于色精度、饱和度、白平衡的标准如下:

测试项测试图卡标准测试条件
色彩还原误差(色精度)ColorCheck△C<=35, △E<=35D65,TL84,A光
色彩饱和度ColorCheckD65、TL84光:95%<饱和度<130%;
A光:90%<饱和度<125%
D65、TL84、A光源 700~1200Lux 图卡亮度均匀性 10%以内
白平衡误差ColorCheckD65、TL84:≤0.15
A:≤0.4
D65、TL84、A光源 700~1200Lux 图卡亮度均匀性 10%以内

五、批量色卡识别并输出结果报表

请添加图片描述

Fig 5.1

imatest有提供批量图像处理分析的功能(Pass/Fail Monitor如上图),但操作和解读个人感觉较繁琐。本人基于前述理论自行开发了一套批量色卡识别并输出判断结果报表的解决方案,色卡分析全流程演示如下:

请添加图片描述

Fig 5.2

附录

【A1】色彩空间转换(RGB→XYZ, XYZ→Lab, XYZ→xyY)

def RGB2XYZ(self, RGB):
    var_R = RGB[0] / 255
    var_G = RGB[1] / 255
    var_B = RGB[2] / 255

    var_R = ((var_R + 0.055) / 1.055)**2.4 if var_R > 0.04045 else var_R / 12.92
    var_G = ((var_G + 0.055) / 1.055)**2.4 if var_G > 0.04045 else var_G / 12.92
    var_B = ((var_B + 0.055) / 1.055)**2.4 if var_B > 0.04045 else var_B / 12.92

    var_R = var_R * 100
    var_G = var_G * 100
    var_B = var_B * 100

    X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805
    Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
    Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505
    return [X, Y, Z]

def XYZ2Lab(self, XYZ, ref_XYZ=[0.9504, 1.0, 1.0889]):
    [X,Y,Z] = XYZ
    [ref_X, ref_Y, ref_Z] = ref_XYZ
    var_X = X/ref_X
    var_Y = Y/ref_Y
    var_Z = Z/ref_Z

    var_X = var_X**(1./3) if var_X > 0.008856 else (7.787 * var_X) + (16/116)
    var_Y = var_Y**(1./3) if var_Y > 0.008856 else (7.787 * var_Y) + (16/116)
    var_Z = var_Z**(1./3) if var_Z > 0.008856 else (7.787 * var_Z) + (16/116)

    L = (116 * var_Y) - 16
    a = 500 * (var_X - var_Y)
    b = 200 * (var_Y - var_Z)
    return [L, a, b]    

def XYZ2xyY(self, XYZ):
    [X,Y,Z] = XYZ
    Y = Y
    x = X/(X+Y+Z)
    y = Y/(X+Y+Z)
    return [x,y,Y]

【A2】Delta-E, Delta-C(CIE1976, CIE1994, CIE2000, CMC)

def delta_C_and_E(self, lab, ideal_lab, method='CIE 1976'):
    delta_l, delta_a, delta_b = lab[0] - ideal_lab[0], lab[1] - ideal_lab[1], lab[2] - ideal_lab[2]
    delta_E = np.sqrt(np.square(delta_l) + np.square(delta_a) + np.square(delta_b))
    delta_C = np.sqrt(np.square(delta_a) + np.square(delta_b))
    return delta_C, delta_E
    
def delta_E_CIE1994(Lab_1, Lab_2, textiles=False):
    L_1, a_1, b_1 = tsplit(to_domain_100(Lab_1))
    L_2, a_2, b_2 = tsplit(to_domain_100(Lab_2))

    k_1 = 0.048 if textiles else 0.045
    k_2 = 0.014 if textiles else 0.015
    k_L = 2 if textiles else 1
    k_C = 1
    k_H = 1

    C_1 = np.hypot(a_1, b_1)
    C_2 = np.hypot(a_2, b_2)

    s_L = 1
    s_C = 1 + k_1 * C_1
    s_H = 1 + k_2 * C_1

    delta_L = L_1 - L_2
    delta_C = C_1 - C_2
    delta_A = a_1 - a_2
    delta_B = b_1 - b_2

    delta_H = np.sqrt(delta_A ** 2 + delta_B ** 2 - delta_C ** 2)

    L = (delta_L / (k_L * s_L)) ** 2
    C = (delta_C / (k_C * s_C)) ** 2
    H = (delta_H / (k_H * s_H)) ** 2

    d_E = np.sqrt(L + C + H)

    return d_E
...

Delta-E(CIE2000, CMC)等在colour-science包里有详尽的代码实现。

【A3】Mean Chroma & WB Error

def mean_camera_chroma(self, lab_list, ideal_lab_list):
    lab_list, ideal_lab_list = np.array(lab_list), np.array(ideal_lab_list)
    chroma_list = np.sqrt(np.square(lab_list[:, 1]) + np.square(lab_list[:, 2]))
    ideal_chroma_list = np.sqrt(np.square(ideal_lab_list[:, 1]) + np.square(ideal_lab_list[:, 2]))
    mean_chroma = np.mean(chroma_list) / np.mean(ideal_chroma_list) * 100
    # mean_chroma = np.mean(chroma_list[:17]) / np.mean(ideal_chroma_list[:17]) * 100
    return np.around(chroma_list, 4), np.round(mean_chroma, 4)

def WB_error(self, RGB_list):
    grey_list = np.array(RGB_list)[18:, ]
    maximum, minimum = np.max(grey_list, axis=1), np.min(grey_list, axis=1)
    WB_error_list = np.divide((maximum - minimum), maximum)
    return np.around(WB_error_list, 4)
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐