Skip to content

02_ccm_useDeltaE

复制本地路径 | 在线编辑

原始文件为 .py 代码,本文是转换后的 Markdown 文件。

```.py
'''
这是用最优化 DeltaE 来计算 CCM 的代码
'''
import os
os.chdir(os.path.dirname(os.path.abspath(file)))

import numpy as np
from func.cp_hw2 import read_colorchecker_gm, readHDR, writeHDR

np.seterr(all='raise')

def LAB2RGBlinear(Lab):
L, a, b = Lab[0], Lab[1], Lab[2]

# D50 LAB to D50 XYZ
Xn = 96.422 / 100
Yn = 100 / 100
Zn = 82.521 / 100

finv = lambda x: np.where(x > 6/29, x**3, 3 * (6/29)**2 * ( x - 4/29 ))
X = Xn * finv( 1/116 * ( L + 16 ) + 1/500 * a )
Y = Yn * finv( 1/116 * ( L + 16 ) )
Z = Zn * finv( 1/116 * ( L + 16 ) - 1/200 * b )

# D50 XYZ to D65 RGB Linear
M1 = np.array([
    [3.1338561, -1.6168667, -0.4906146],
    [-0.9787684,  1.9161415,  0.0334540],
    [0.0719453, -0.2289914,  1.4052427]
])

# (3x3) @ (3x1) = (3x1)
rgb = np.dot(M1, np.vstack((X, Y, Z)))
return rgb[:, 0]

def RGBlinear2LAB(rgb):
# D65 RGB Linear to D50 XYZ
M1 = np.array([
[3.1338561, -1.6168667, -0.4906146],
[-0.9787684, 1.9161415, 0.0334540],
[0.0719453, -0.2289914, 1.4052427]
])
M1 = np.linalg.inv(M1)
XYZ = np.dot(M1, rgb)
X, Y, Z = XYZ[0], XYZ[1], XYZ[2]

# D50 XYZ to D50 LAB
Xn = 96.422 / 100
Yn = 100 / 100
Zn = 82.521 / 100

f = lambda x: np.where(x > (6/29)**3, x**(1/3), (1/3) * x * (6/29)**(-2) + 4/29)
L = 116 * f(Y / Yn) - 16
a = 500 * (f(X / Xn) - f(Y / Yn))
b = 200 * (f(Y / Yn) - f(Z / Zn))

return np.array([L, a, b])

该函数用于生成色卡块图,用于检测正确性;同时也顺便生成色卡每个块的平均值

def make_patches(coors, img, onerow=6):
rows, cols = 24//onerow, onerow

# patches: 色卡的图,patches_rgb:色卡的平均图(24*3)
patches = np.zeros((rows*80, cols*80, 3))
patches_rgb = np.zeros((24, 3))
for i, coor in enumerate(coors):
    nowpatch = img[coor[1]:coor[3], coor[0]:coor[2]]

    x = (i // onerow) * 80
    y = (i % onerow) * 80
    patches[x:x+80, y:y+80] = nowpatch[0:80, 0:80]
    patches_rgb[i] = np.mean(nowpatch[0:80, 0:80].reshape(-1, 3), axis=0)
return patches, patches_rgb

coors = []
with open('../data/24color_coor.txt', 'r') as f:
for i, line in enumerate(f):
coors.append([int(x) for x in line.split()])

保存的色卡坐标,索引有点不对,现在重排一下

coors = np.array(coors).reshape(6, 4, 4).transpose(1, 0, 2).reshape(24, 4)

加载基准 RGB

ok_rgb, ok_Lab = read_colorchecker_gm()

处理图片保存下来参数

os.makedirs('../result/stage-2', exist_ok=True)
for filename in os.listdir('../result/stage-1'):
print(filename)

hdrimg = readHDR(f'../result/stage-1/{filename}')

# 先做 AWB,还是一样,先提取色卡,然后只用白色块,最后一行第一个
now_patches, patches_rgb = make_patches(coors, hdrimg)
wr, wg, wb = patches_rgb[3*6+0]

rgain, bgain = wg / wr, wg / wb
hdrimg[..., 0] *= rgain
hdrimg[..., 2] *= bgain

# 根据coors记录的色卡坐标,生成色卡图,顺便把色卡的各个平均值计算一下
now_patches, patches_rgb = make_patches(coors, hdrimg)

# # 方法一:用最小二乘法计算 CCM
# A = np.hstack((patches_rgb, np.ones((24, 1))))
# u, *_ = np.linalg.lstsq(A, ok_rgb, rcond=None)

# 方法二:用最优化 DeltaE 计算 CCM
# ! np.longdouble 类型,很重要,否则很影响最优化结果!!
patches_rgb = patches_rgb.astype(np.longdouble)
# DeltaE 指标
def DeltaE(x):
    # ! 这里也要转成 np.longdouble 类型
    nowM = np.array([
        [1-x[0]-x[1], x[0], x[1]],
        [x[2], 1-x[2]-x[3], x[3]],
        [x[4], x[5], 1-x[4]-x[5]]
    ]).astype(np.longdouble)

    sum_deltaE = 0
    for i in range(24):
        now_rgb = np.dot(nowM, patches_rgb[i])
        now_Lab = RGBlinear2LAB(now_rgb)
        sum_deltaE += np.square(now_Lab - ok_Lab[i]).sum()
    return sum_deltaE

# 初始化参数,开始最优化迭代
u = np.array([0, 0, 0, 0, 0, 0])
import scipy.optimize as optimize
x = optimize.minimize(DeltaE, u, method='SLSQP', options={'maxiter': 2000}).x
u = np.array([
    [1-x[0]-x[1], x[0], x[1], 0],
    [x[2], 1-x[2]-x[3], x[3], 0],
    [x[4], x[5], 1-x[4]-x[5], 0],
]).T

# Make CCM
rows, cols = hdrimg.shape[0:2]
hdrimg = np.hstack((hdrimg.reshape(-1, 3), np.ones((rows*cols, 1))))
hdrimg = np.dot(hdrimg.astype(np.longdouble), u.astype(np.longdouble))

# ! 很重要:否则会有异色!!!
hdrimg[hdrimg < 0] = 0
hdrimg = hdrimg.astype(np.float32).reshape((rows, cols, 3))

# WriteHDR, Good Job.
writeHDR(f'../result/stage-2/{filename}', hdrimg)```

Comments