使用根多项式回归的颜色检查器的颜色校准没有给出正确的结果

Color calibration with color checker using using Root-Polynomial Regression not giving correct results

对于一个量化项目,我需要经过颜色校正的图像,无论光照条件如何,它都能一遍又一遍地产生相同的结果。

每个图像都包含一个 X-Rite 颜色检查器,其颜色以矩阵格式已知:

Reference=[[170, 189, 103],[46, 163, 224],[161, 133, 8],[52, 52, 52],[177, 128, 133],[64, 188, 157],[149, 86, 187],[85, 85, 85],[67, 108, 87],[108, 60, 94],[31, 199, 231],[121, 122, 122], [157, 122, 98],[99, 90, 193],[60, 54, 175],[160, 160, 160],[130, 150, 194],[166, 91, 80],[70, 148, 70],[200, 200, 200],[68, 82, 115],[44, 126, 214],[150, 61, 56],[242, 243, 243]]

对于每张图像,我都会为当前的色卡计算相同的矩阵作为示例:

Actual_colors=[[114, 184, 137], [2, 151, 237], [118, 131, 55], [12, 25, 41], [111, 113, 177], [33, 178, 188], [88, 78, 227], [36, 64, 85], [30, 99, 110], [45, 36, 116], [6, 169, 222], [53, 104, 138], [98, 114, 123], [48, 72, 229], [29, 39, 211], [85, 149, 184], [66, 136, 233], [110, 79, 90], [41, 142, 91], [110, 180, 214], [7, 55, 137], [0, 111, 238], [82, 44, 48], [139, 206, 242]]

然后我使用从输入和输出矩阵的系数导出的 color correction matrix 校准整个图像:

for im in calibrated_img:
    im[:]=colour.colour_correction(im[:], Actual_colors, Reference, "Finlayson 2015")

结果如下:

上面的图像代表输入,下面的图像代表输出。 照明在色彩校正的最终结果中起着关键作用,但左侧的前两张图像应生成相同的输出。一旦图像变得太暗,白色就会以某种方式转换为红色。我无法理解为什么。

我尝试在处理前应用伽玛校正但没有成功。 其他两个模型 Cheung 2004 和 Vandermonde 给出的结果更差,偏最小二乘法也是如此。黄色辐射灯对图像进行了很好的校正,但最终结果不是干净的白色,而是图像上有蓝色的雾霾。白色应该是白色的..我可以做些什么来进一步改善这些结果?


编辑 23-08-2020: 根据@Kel Solaar 他的评论,我对我的脚本进行了更改,以包括他提到的步骤如下

#Convert image from int to float
Float_image=skimage.img_as_float(img)

#Normalise image to have pixel values from 0 to 1
Normalised_image = (Float_image - np.min(Float_image))/np.ptp(Float_image)

#Decoded the image with sRGB EOTF
Decoded_img=colour.models.eotf_sRGB(Normalised_image)  

#Performed Finlayson 2015 color correction to linear data:
for im in Decoded_img:
    im[:]=colour.colour_correction(im[:], Image_list, Reference, "Finlayson 2015")

#Encoded image back to sRGB
Encoded_img=colour.models.eotf_inverse_sRGB(Decoded_img)  

#Denormalized image to fit 255 pixel values
Denormalized_image=Encoded_img*255

#Converted floats back to integers
Integer_image=Denormalised_image.astype(int)

这大大提高了图像质量,如下所示:

但是,不幸的是,lighting/color 校正图像之间的差异仍然存在。

可以找到原始图像here,但请注意它们是颠倒的。

图像中色卡测量值:

IMG_4244.JPG
[[180, 251, 208], [62, 235, 255], [204, 216, 126], [30, 62, 97], [189, 194, 255], [86, 250, 255], [168, 151, 255], [68, 127, 167], [52, 173, 193], [111, 87, 211], [70, 244, 255], [116, 185, 228], [182, 199, 212], [102, 145, 254], [70, 102, 255], [153, 225, 255], [134, 214, 255], [200, 156, 169], [87, 224, 170], [186, 245, 255], [44, 126, 235], [45, 197, 254], [166, 101, 110], [224, 255, 252]]

IMG_4243.JPG
[[140, 219, 168], [24, 187, 255], [148, 166, 73], [17, 31, 53], [141, 146, 215], [42, 211, 219], [115, 101, 255], [33, 78, 111], [24, 118, 137], [63, 46, 151], [31, 203, 255], [67, 131, 172], [128, 147, 155], [61, 98, 255], [42, 59, 252], [111, 181, 221], [88, 168, 255], [139, 101, 113], [47, 176, 117], [139, 211, 253], [19, 78, 178], [12, 146, 254], [110, 60, 64], [164, 232, 255]]

IMG_4241.JPG
[[66, 129, 87], [0, 90, 195], [65, 73, 26], [9, 13, 18], [60, 64, 117], [20, 127, 135], [51, 38, 176], [15, 27, 39], [14, 51, 55], [21, 15, 62], [1, 112, 180], [29, 63, 87], [54, 67, 69], [20, 33, 179], [10, 12, 154], [38, 92, 123], [26, 81, 178], [58, 44, 46], [23, 86, 54], [67, 127, 173], [5, 26, 77], [2, 64, 194], [43, 22, 25], [84, 161, 207]]

IMG_4246.JPG
[[43, 87, 56], [2, 56, 141], [38, 40, 20], [3, 5, 6], [31, 31, 71], [17, 85, 90], [19, 13, 108], [7, 13, 20], [4, 24, 29], [8, 7, 33], [1, 68, 123], [14, 28, 46], [28, 34, 41], [6, 11, 113], [0, 1, 91], [27, 53, 83], [11, 44, 123], [32, 21, 23], [11, 46, 26], [32, 77, 115], [2, 12, 42], [0, 29, 128], [20, 9, 11], [49, 111, 152]]

色卡(或参考)的实际颜色在此 post 的顶部给出,并且与为图像给出的值的顺序相同。


编辑 30-08-2020,我应用了@nicdall 他的评论:

#Remove color chips which are outside of RGB range
New_reference=[]
New_Actual_colors=[]
for L,K in zip(Actual_colors, range(len(Actual_colors))):
    if any(m in L for m in [0, 255]):
        print(L, "value outside of range")
    else:
        New_reference.append(Reference[K])
        New_Actual_colors.append(Actual_colors[K])

除此之外,我意识到我使用的是色卡中的单个像素,所以我开始在每个色卡上取 15 个像素并对它们进行平均,以确保达到良好的平衡。代码太长 post 这里完全没有,但在这个方向上有些东西(不要在这里判断我的错误编码):

for i in Chip_list:
    R=round(sum([rotated_img[globals()[i][1],globals()[i][0],][0],
        rotated_img[globals()[i][1]+5,globals()[i][0],][0],
        rotated_img[globals()[i][1]+10,globals()[i][0],][0],
        rotated_img[globals()[i][1],(globals()[i][0]+5)][0],
        rotated_img[globals()[i][1],(globals()[i][0]+10)][0],
        rotated_img[globals()[i][1]+5,(globals()[i][0]+5)][0],
        rotated_img[globals()[i][1]+10,(globals()[i][0]+10)][0]])/(number of pixels which are summed up))

结果令人失望,因为修正似乎变得更糟,但如下所示:

New_reference = [[170, 189, 103], [161, 133, 8], [52, 52, 52], [177, 128, 133], [64, 188, 157], [85, 85, 85], [67, 108, 87], [108, 60, 94], [121, 122, 122], [157, 122, 98], [60, 54, 175], [160, 160, 160], [166, 91, 80], [70, 148, 70], [200, 200, 200], [68, 82, 115], [44, 126, 214], [150, 61, 56]]
#For Image: IMG_4243.JPG:
New_Actual_colors= [[139, 218, 168], [151, 166, 74], [16, 31, 52], [140, 146, 215], [44, 212, 220], [35, 78, 111], [25, 120, 137], [63, 47, 150], [68, 132, 173], [128, 147, 156], [40, 59, 250], [110, 182, 222], [141, 102, 115], [48, 176, 118], [140, 211, 253], [18, 77, 178], [12, 146, 254], [108, 59, 62]]

#The following values were omitted in IMG_4243:
[23, 187, 255] value outside of range
[115, 102, 255] value outside of range
[30, 203, 255] value outside of range
[61, 98, 255] value outside of range
[88, 168, 255] value outside of range
[163, 233, 255] value outside of range

我已经开始接近问题的核心了,但我不是数学家,但是更正本身似乎就是问题所在.. 这是颜色包生成和使用的 IMG4243.jpg 的颜色校正矩阵:

CCM=colour.characterisation.colour_correction_matrix_Finlayson2015(New_Actual_colors, New_reference, degree=1 ,root_polynomial_expansion=True)
print(CCM)
[[ 1.10079803 -0.03754644  0.18525637]
 [ 0.01519612  0.79700086  0.07502735]
 [-0.11301282 -0.05022718  0.78838144]]

根据我对彩包代码的理解,New_Actual_colors用CCM转换如下:

Converted_colors=np.reshape(np.transpose(np.dot(CCM, np.transpose(New_Actual_colors))), shape)

当我们将Converted_colors与New_reference进行比较时,我们可以看到修正已经走了很长一段路,但差异仍然存在(所以最终目标是转换New_Actual_colors 颜色校正矩阵 (CCM) 到 Converted_colors 应该与 New_reference 完全匹配):

print("New_reference =",New_reference)
print("Converted_colors =",Converted_colors)
New_reference =    [[170, 189, 103],[161, 133, 8],[52, 52, 52],[177, 128, 133],[64, 188, 157],[85, 85, 85],[67, 108, 87],[108, 60, 94],[121, 122, 122],[157, 122, 98],[60, 54, 175],[160, 160, 160],[166, 91, 80],[70, 148, 70],[200, 200, 200],[68, 82, 115],[44, 126, 214],[150, 61, 56]]
Converted_colors = [[176, 188, 106],[174, 140, 33],[26, 29, 38],[188, 135, 146],[81, 186, 158],[56, 71, 80],[48, 106, 99],[95, 50, 109],[102, 119, 122],[164, 131, 101],[88, 66, 190],[155, 163, 153],[173, 92, 70],[68, 150, 79],[193, 189, 173],[50, 75, 134],[55, 136, 192],[128, 53, 34]]

减去差异就很明显了,问题是如何克服这些差异?:

list(np.array(New_reference) - np.array(Converted_colors))
[array([-6,  1, -3]),
 array([-13,  -7, -25]),
 array([26, 23, 14]),
 array([-11,  -7, -13]),
 array([-17,   2,  -1]),
 array([29, 14,  5]),
 array([ 19,   2, -12]),
 array([ 13,  10, -15]),
 array([19,  3,  0]),
 array([-7, -9, -3]),
 array([-28, -12, -15]),
 array([ 5, -3,  7]),
 array([-7, -1, 10]),
 array([ 2, -2, -9]),
 array([ 7, 11, 27]),
 array([ 18,   7, -19]),
 array([-11, -10,  22]),
 array([22,  8, 22])]

这里有一些建议:

  • 正如我在上面的评论中所述,Finlayson (2015) 的 Root-Polynomial 变体存在一个实施问题,应该在 develop branch.
  • 中修复
  • 您正在将整数和编码值传递给 colour.colour_correction 定义。我强烈建议您:
    • 将数据集转换为浮点表示。
    • 将其从范围 [0, 255] 缩放到范围 [0, 1]。
    • 使用 sRGB EOTF 对其进行解码。
    • 对该线性数据执行颜色校正。
    • 编码并缩小为整数表示。
  • 您的图像似乎是一个曝光楔形,理想情况下,您会为适当的参考曝光计算一个矩阵,将其他图像曝光归一化并在其上应用该矩阵。

更多关于问题物理方面的额外建议:我看到高曝光和低曝光图像中的一些 RGB 值超出了相机的不饱和范围(0 和 255 值)。这意味着在拍摄图像时,一些关于实际测量颜色的信息会丢失,因为一些校准色块要么曝光过度,要么曝光不足。这是 RGB 比色法中的一个已知问题,实际上在 (Finlayson, 2015) 中提到: " 额外的假设是 v 和 kv在相机的不饱和范围内

如果可能,请在拍摄图像时尝试查看直方图,以便所有像素的值都在不饱和范围内(最多 [1, 254])。

否则,如果无法拍摄新图像,您可以尝试在校准过程中忽略饱和色块(R、G 或 B 值中的任何一个为 0 或 255)(确保您忽略图像和参考中的补丁)。这可以改善您对整体图像的校准,因为您没有让模型拟合饱和值。