01_combine_hdr
复制本地路径 | 在线编辑
原始文件为 .py 代码,本文是转换后的 Markdown 文件。
'''
注意点
1. 保存 HDR 图像,请务必保存为 np.float32!!! 不然 OpenHDR 打开会有问题,有大块黑色或者单色先检查一下这个情况
2. OpenHDR 打不开或者 opencv 返回 None 可能是因为不同 HDR 格式,看看文件是不是32-bit_rle_rgbe,这是另一种格式
'''
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
import numpy as np
'''
1.1 use dcraw to convert nef to tiff
'''
picnum = 16
if not os.path.exists('../data/door_stack/exposure0.tiff'):
for i in range(picnum):
os.system(f'mv ../data/door_stack/exposure{i+1}.jpg ../data/door_stack/exposure{i}.jpg')
os.system(f'mv ../data/door_stack/exposure{i+1}.nef ../data/door_stack/exposure{i}.nef')
for i in range(picnum):
# -w: white balance (dont use -D which prevent scaling)
# -q [0-3]: Demosaicing(Interploation), high number -> high quality.
# However, dcraw's algorithm is too old!! AHD is its best algorithm...
# -o [0-6]: 0->raw, 1->sRGB, ...
os.system(f'dcraw -v -T -4 -w -q 3 -o 1 ../data/door_stack/exposure{i}.nef > /dev/null')
print('1.1 Done')
'''
1.4 different wfunc
'''
exptime = np.zeros(picnum)
for i in range(picnum):
exptime[i] = 2**i / 2048
import math
import cv2
# 处理完全欠曝光和过曝光的情况
# 找出最小值都小于0.05的列,最大值都大于0.95的列
def find_extreme(I, minv=0.05, maxv=0.95):
max_value = np.max(I, axis=0)
min_value = np.min(I, axis=0)
return max_value <= minv, min_value >= maxv
def w_uniform(I, minv=0.05, maxv=0.95):
return np.where((minv<I)&(I<maxv), 1, 0)
def w_tent(I, minv=0.05, maxv=0.95):
return np.where((minv<I)&(I<maxv), np.minimum(I, 1-I), 0)
def w_gaussian(I, minv=0.05, maxv=0.95):
z = np.exp(-4*(I-0.5)*(I-0.5)/0.25)
return np.where((minv<I) & (I<maxv), z, 0)
def w_photon(I, minv=0.05, maxv=0.95):
return np.where((minv<I)&(I<maxv), exptime[:, np.newaxis], 0)
wfuncs = {
'uniform': w_uniform,
'tent': w_tent,
'gaussian': w_gaussian,
'photon': w_photon
}
'''
1.2 Relinear jpg
'''
# 最小二乘法求解非线性响应,具体求解在 relinear.py 中
# 结果放在 data/response 中
if not os.path.exists('../data/response'):
os.system('python 01_relinear_jpg.py')
'''
1.3 Generate HDR
'''
# 公式 5 和 6 的计算,分子、分母、欠曝光索引、过曝光索引
def fill_extreme(result, toomin, toomax):
result[toomin] = result[(~toomin)&(~toomax)].min()
result[toomax] = result[(~toomin)&(~toomax)].max()
return result
for imgtype in ['jpg', 'tiff']:
# 先读取一张图片,获取长宽信息
temp_pic = cv2.imread(f'../data/door_stack/exposure0.{imgtype}', -1)
max_value = np.iinfo(temp_pic.dtype).max
rows, cols = temp_pic.shape[0:2]
for wfuncname, wfunc in wfuncs.items():
print(imgtype, wfuncname)
hdrimg1 = np.zeros((rows, cols, 3), dtype=np.float32)
hdrimg2 = np.zeros((rows, cols, 3), dtype=np.float32)
# 响应曲线, tiff 为线性, jpg 就是计算而来, 即公式1.5和1.6中 Lin=response[LDR]
response = None
if imgtype == 'jpg':
response = np.load(f'../data/response/{wfuncname}.npy')
elif imgtype == 'tiff':
response = np.array([i/max_value for i in range(max_value+1)])
# 占用内存太大了,全部处理,空间不够,分块进行操作
onerow, onecol = rows//4, cols//4
for ridx in range(0, 4):
for cidx in range(0, 4):
print(ridx, cidx)
rbegin, cbegin = ridx*onerow, cidx*onecol
pics = np.zeros((picnum, onerow, onecol, 3), dtype=int)
for i in range(picnum):
onepic = cv2.imread(f'../data/door_stack/exposure{i}.{imgtype}', -1)
pics[i] = onepic[rbegin:rbegin+onerow, cbegin:cbegin+onecol]
# 每次处理一个颜色
for channel in range(0, 3):
nowpics = pics[..., channel].reshape(picnum, -1)
# 找出欠曝和过曝的
toomin, toomax = find_extreme(nowpics/max_value)
# 求解 w
w = wfunc(nowpics / max_value)
# 由于欠曝和过曝,w 有的列都为 1,将这些点先填一个假的值
w[0, toomax] = w[0, toomin] = 1
# 分母
wsum = np.sum(w, axis=0)
# 线性合并, X 是分子
X = np.sum(w * response[nowpics] / exptime[:, np.newaxis], axis=0)
result = fill_extreme(X/wsum, toomin, toomax).reshape(onerow, onecol)
hdrimg1[rbegin:rbegin+onerow, cbegin:cbegin+onecol, channel] = result
# 对数合并, X 是分子
response[response==0] = 1
log_delta = np.log2(response[nowpics]) - np.log2(exptime[:, np.newaxis])
X = np.sum(w * log_delta, axis=0)
result = fill_extreme(np.power(2, X/wsum), toomin, toomax).reshape(onerow, onecol)
hdrimg2[rbegin:rbegin+onerow, cbegin:cbegin+onecol, channel] = result
# hdrimg1 = hdrimg1 / hdrimg1.max()
# hdrimg2 = hdrimg2 / hdrimg2.max()
os.makedirs(f'../result/stage-1/', exist_ok=True)
cv2.imwrite(f'../result/stage-1/{imgtype}-{wfuncname}-line.hdr', hdrimg1)
cv2.imwrite(f'../result/stage-1/{imgtype}-{wfuncname}-log.hdr', hdrimg2)