01_relinear_jpg
原始文件为 .py 代码,本文是转换后的 Markdown 文件。
```.py
'''
将 JPG 文件线性化,涉及到计算相机响应曲线,OpenCV 也有现成的 API,本文件也进行了计算
本文件的一些计算方式和 OpenCV 不太一样(因为完全参考本文件夹的实验手册,里面有些地方要求和 OpenCV 不一样,比如这里会比较不同的权重函数等等)
在 relinear_as_opencv.py 中复现了和 OpenCV 一样的相机响应曲线
'''
先读取文件、设置曝光时间
import os
os.chdir(os.path.dirname(os.path.abspath(file)))
import cv2
import time
import math
import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize
picnum = 16
images = [cv2.imread(f'../data/door_stack/exposure{i}.jpg', -1) for i in range(picnum)]
exptime = np.array([(2**i)/4096 for i in range(picnum)], dtype=np.float32)
获取图片宽和高
rows, cols = images[0].shape[0:2]
'''
直接用 opencv 的 API 来做
'''
参考: https://learnopencv.com/high-dynamic-range-hdr-imaging-using-opencv-cpp-python/
校准(对齐)各张图片,不过有的时候结果可能会反而不好
cv2.createAlignMTB().process(images, images)
不仅仅只有 Debevec 方法,用的时候再去细看
获取响应函数 -> 制作 HDR
cv_response = cv2.createCalibrateDebevec().process(images, exptime)
result = cv2.createMergeDebevec().process(images, exptime, cv_response)
print(cv_response.shape)
plt.plot(np.log(cv_response[..., 0]))
plt.plot(np.log(cv_response[..., 1]))
plt.plot(np.log(cv_response[..., 2]))
plt.title('camera response by opencv')
plt.show()
ToneMapping,有很多,不列举了,看上面的网页,解释的非常非常好!
tonemapping_1 = cv2.createTonemapReinhard(1.5, 0, 0, 0).process(result)
cv2.imwrite('./opencv_result.png', tonemapping_1*255)
'''
权重函数,最小化要用到
'''
def w_uniform(z, minv=0.05, maxv=0.95):
return 1 if minv<z<maxv else 0
def w_tent(z, minv=0.05, maxv=0.95):
return min(z, 1-z) if minv<z<maxv else 0
def w_gaussian(z, minv=0.05, maxv=0.95):
return np.exp(-4(z-0.5)(z-0.5)/0.25) if minv<z<maxv else 0
def w_photon(z, minv=0.05, maxv=0.95):
return 1 if minv<z<maxv else 0
wfuncs = {
'uniform': w_uniform,
'tent': w_tent,
'gaussian': w_gaussian,
'photon': w_photon
}
'''
自己来求响应,两种方法第一种:最小二乘法矩阵形式
'''
def compute_by_matrix(I, my_lambda, w_func):
N, P = I.shape[0:2]
A = np.zeros((N*P+255, 256+N))
b = np.zeros((N*P+255, 1))
for i in range(N):
for j in range(P):
Iij = int(I[i, j])
w = w_func(Iij / 255)
A[i*P+j, Iij] = w
A[i*P+j, 256+i] = -w
b[i*P+j] = w * math.log2(exptime[j])
for i in range(254):
A[N*P+i, i+0] = my_lambda * w_func((i+1)/255)
A[N*P+i, i+1] = -2 * my_lambda * w_func((i+1)/255)
A[N*P+i, i+2] = my_lambda * w_func((i+1)/255)
A[-1, 128] = 1
x = np.linalg.lstsq(A, b, rcond=None)[0]
return x[0:256, 0]
'''
自己来求响应,两种方法第二种:用 scipy minimize 来迭代求解
'''
def compute_by_scipy(I, my_lambda, w_func):
def compute_func(data):
# func: 前256个数字是 g(i),后面是 logL
g, logL = data[0:256], data[256:]
result = 0
# 第一项
for ij in range(N):
for k in range(P):
pixel = int(I[ij, k])
w = w_func(pixel / 255)
result += ( w*(g[pixel] - logL[ij] - math.log2(exptime[k])) )**2
# 第二项
for z in range(1, 255):
Dg = g[z+1] - 2*g[z] + g[z-1]
w = w_func(z / 255)
result += my_lambda * (( w * Dg )**2)
return result
N, P = I.shape[0:2]
# 限制:g(127) = 0
cons= [{'type':'eq', 'fun': lambda m: m[127]}]
# # 限制: g 是单调递增函数
# for i in range(1, 256):
# # create lambda in for loop 需要注意!!Cite: stackoverflow 2295290
# cons.append({'type':'ineq', 'fun': lambda m,i=i: m[i] - m[i-1] - 1e-8})
# 迭代次数
opts = {'maxiter':1000, 'disp':False}
# 初始化
this_input = np.zeros(256+N)
res = scipy.optimize.minimize(compute_func, this_input, method='SLSQP', constraints=cons, options=opts)
return res.x[0:256]
在 P 张曝光图像上选择 N 个点
P = picnum
N = 60
I 表示选择的点,注意 I(i,j) 中 i 表示像素位置,j 表示第几张图片
I = np.zeros((N, P))
要求对三个通道像素都进行处理
方式:1.直接平均;2.转成 Y 分量;3.每个分量都取一些点
这里用第三个方式
for channel in range(0, 3):
# 每个通道平均取 N/3 个点
assert(N % 3 == 0)
nowN = N // 3
points = np.random.randint([0, 0], [rows, cols], size=(nowN, 2))
for pidx in range(P):
onepic = images[pidx][..., channel][points[:, 0], points[:, 1]]
I[nowN*channel:nowN*(channel+1), pidx] = onepic
# 计算结果,并且比较结果和 opencv 的标定结果
for my_lambda in [10, 20, 30]:
for name, wfunc in wfuncs.items():
# 注意:我们是三个通道合起来处理得出一个响应函数 g;而 OpenCV 是三个通道分别处理
plt.figure()
plt.title(f'{name}-lambda={my_lambda}')
plt.plot(np.log(cv_response[..., 1]), label='Opencv-G')
t1 = time.time()
matrix_response = compute_by_matrix(I, my_lambda=my_lambda, w_func=wfunc)
t2 = time.time()
scipy_response = compute_by_scipy(I, my_lambda=my_lambda, w_func=wfunc)
t3 = time.time()
plt.plot(matrix_response, label=f'My-bymatrix-time={round(t2-t1, 3)}')
plt.plot(scipy_response, label=f'My-byscipy-time={round(t3-t2, 3)}')
plt.legend()
plt.show()
存储结果
good_lambda = {
'uniform': 25,
'tent': 25,
'gaussian': 25,
'photon': 25
}
os.makedirs('../data/response', exist_ok=True)
for name, wfunc in wfuncs.items():
print(name, 'doing...............')
matrix_response = compute_by_matrix(I, my_lambda=good_lambda[name], w_func=wfunc)
np.save(f'../data/response/{name}', np.exp(matrix_response))```