YOND / data_process /process.py
hansen97's picture
Initial clean commit
0e07d71
"""Forward processing of raw data to sRGB images.
Unprocessing Images for Learned Raw Denoising
http://timothybrooks.com/tech/unprocessing
"""
import rawpy
import rawpy.enhance
import exifread
import numpy as np
import torch
import torch.nn as nn
import torch.distributions as tdist
from scipy import stats
from utils import *
import random
from .unprocess import random_gains
Dual_ISO_Cameras = ['SonyA7S2']
HALF_CLIP = 2
def bpc_aug(data, ratio=1e-6, wp=1):
B, C, H, W = data.shape
npoints = int(2 * np.random.rand() * ratio * data.numel())
for i in range(npoints):
x = np.random.randint(H)
y = np.random.randint(W)
c = np.random.randint(C)
b = np.random.randint(B)
data[b, c, x, y] = wp
return data
def data_aug(data, choice, bias=0, rot=False):
if choice[0] == 1:
data = np.flip(data, axis=2+bias)
if choice[1] == 1:
data = np.flip(data, axis=3+bias)
return data
def inverse_VST_torch(x, noiseparam, iso_list, wp=1):
x = x * wp
b = len(iso_list)
for i in range(b):
iso = iso_list[i].item()
gain = noiseparam[iso]['Kmax']
sigma = noiseparam[iso]['sigGs']
x[i] = (x[i] / 2.0)**2 - 3.0/8.0 - sigma**2 / gain**2
x[i] = x[i] * gain
x = x / wp
return x
def pack_raw_bayer(raw, wp=1023, clip=True):
#pack Bayer image to 4 channels
im = raw.raw_image_visible.astype(np.float32)
raw_pattern = raw.raw_pattern
R = np.where(raw_pattern==0)
G1 = np.where(raw_pattern==1)
B = np.where(raw_pattern==2)
G2 = np.where(raw_pattern==3)
white_point = wp
img_shape = im.shape
H = img_shape[0]
W = img_shape[1]
out = np.stack((im[R[0][0]:H:2, R[1][0]:W:2], #RGBG
im[G1[0][0]:H:2, G1[1][0]:W:2],
im[B[0][0]:H:2, B[1][0]:W:2],
im[G2[0][0]:H:2, G2[1][0]:W:2]), axis=0).astype(np.float32)
black_level = np.array(raw.black_level_per_channel)[:,None,None].astype(np.float32)
out = (out - black_level) / (white_point - black_level)
out = np.clip(out, 0.0, 1.0) if clip else out
return out
def postprocess_bayer(rawpath, img4c, white_point=1023):
if torch.is_tensor(img4c):
img4c = img4c.detach()
img4c = img4c[0].cpu().float().numpy()
img4c = np.clip(img4c, 0, 1)
#unpack 4 channels to Bayer image
raw = rawpy.imread(rawpath)
raw_pattern = raw.raw_pattern
R = np.where(raw_pattern==0)
G1 = np.where(raw_pattern==1)
G2 = np.where(raw_pattern==3)
B = np.where(raw_pattern==2)
black_level = np.array(raw.black_level_per_channel)[:,None,None]
img4c = img4c * (white_point - black_level) + black_level
img_shape = raw.raw_image_visible.shape
H = img_shape[0]
W = img_shape[1]
raw.raw_image_visible[R[0][0]:H:2, R[1][0]:W:2] = img4c[0, :,:]
raw.raw_image_visible[G1[0][0]:H:2,G1[1][0]:W:2] = img4c[1, :,:]
raw.raw_image_visible[B[0][0]:H:2,B[1][0]:W:2] = img4c[2, :,:]
raw.raw_image_visible[G2[0][0]:H:2,G2[1][0]:W:2] = img4c[3, :,:]
# out = raw.postprocess(use_camera_wb=False, user_wb=[1,1,1,1], half_size=True, no_auto_bright=True, output_bps=8, bright=1, user_black=None, user_sat=None)
# out = raw.postprocess(use_camera_wb=False, user_wb=[1.96875, 1, 1.444, 1], half_size=True, no_auto_bright=True, output_bps=8, bright=1, user_black=None, user_sat=None)
out = raw.postprocess(use_camera_wb=True, half_size=False, no_auto_bright=True, output_bps=8, bright=1, user_black=None, user_sat=None)
return out
def postprocess_bayer_v2(rawpath, img4c):
with rawpy.imread(rawpath) as raw:
out_srgb = raw2rgb_postprocess(img4c.detach(), raw)
return out_srgb
def apply_gains(bayer_images, wbs):
"""Applies white balance to a batch of Bayer images."""
N, C, _, _ = bayer_images.shape
wbs = wbs.repeat((N,1)).view(N, C, 1, 1)
outs = bayer_images * wbs
return outs
def apply_ccms(images, ccms):
"""Applies color correction matrices."""
images = images.permute(
0, 2, 3, 1) # Permute the image tensor to BxHxWxC format from BxCxHxW format
images = images[:, :, :, None, :]
ccms = ccms[:, None, None, :, :]
outs = torch.sum(images * ccms, dim=-1)
# Re-Permute the tensor back to BxCxHxW format
outs = outs.permute(0, 3, 1, 2)
return outs
def gamma_compression(images, gamma=2.2):
"""Converts from linear to gamma space."""
outs = torch.clamp(images, min=1e-8) ** (1 / gamma)
# outs = (1 + gamma[0]) * np.power(images, 1.0/gamma[1]) - gamma[0] + gamma[2]*images
outs = torch.clamp((outs*255).int(), min=0, max=255).float() / 255
return outs
def raw2LRGB(bayer_images):
"""RGBG -> linear RGB"""
lin_rgb = torch.stack([
bayer_images[:,0,...],
torch.mean(bayer_images[:, [1,3], ...], dim=1),
bayer_images[:,2,...]], dim=1)
return lin_rgb
def process(bayer_images, wbs, cam2rgbs, gamma=2.2):
"""Processes a batch of Bayer RGBG images into sRGB images."""
# White balance.
bayer_images = apply_gains(bayer_images, wbs)
# Binning
bayer_images = torch.clamp(bayer_images, min=0.0, max=1.0)
images = raw2LRGB(bayer_images)
# Color correction.
images = apply_ccms(images, cam2rgbs)
# Gamma compression.
images = torch.clamp(images, min=0.0, max=1.0)
images = gamma_compression(images, gamma)
return images
def raw2rgb(packed_raw, raw):
"""Raw2RGB pipeline (preprocess version)"""
wb = np.array(raw.camera_whitebalance)
wb /= wb[1]
cam2rgb = raw.color_matrix[:3, :3]
if cam2rgb[0,0] == 0:
cam2rgb = np.eye(3, dtype=np.float32)
if isinstance(packed_raw, np.ndarray):
packed_raw = torch.from_numpy(packed_raw).float()
wb = torch.from_numpy(wb).float().to(packed_raw.device)
cam2rgb = torch.from_numpy(cam2rgb).float().to(packed_raw.device)
out = process(packed_raw[None], wbs=wb[None], cam2rgbs=cam2rgb[None], gamma=2.2)[0, ...].numpy()
return out
def raw2rgb_v2(packed_raw, wb, ccm):
if torch.is_tensor(packed_raw):
packed_raw = packed_raw.detach().cpu().float()
else:
packed_raw = torch.from_numpy(packed_raw).float()
wb = torch.from_numpy(wb).float()
cam2rgb = torch.from_numpy(ccm).float()
out = process(packed_raw[None], wbs=wb[None], cam2rgbs=cam2rgb[None], gamma=2.2)[0, ...].numpy()
return out.transpose(1,2,0)
def raw2rgb_postprocess(packed_raw, raw):
"""Raw2RGB pipeline (postprocess version)"""
assert packed_raw.ndimension() == 4
wb = np.array(raw.camera_whitebalance)
wb /= wb[1]
cam2rgb = raw.color_matrix[:3, :3]
if cam2rgb[0,0] == 0:
cam2rgb = np.eye(3, dtype=np.float32)
wb = torch.from_numpy(wb[None]).float().to(packed_raw.device)
cam2rgb = torch.from_numpy(cam2rgb[None]).float().to(packed_raw.device)
out = process(packed_raw, wbs=wb, cam2rgbs=cam2rgb, gamma=2.2)
# out = raw.postprocess(use_camera_wb=True, half_size=False, no_auto_bright=True, output_bps=16)
return out
# def get_specific_noise_params(camera_type=None, iso='100'):
# cam_noisy_params = {}
# cam_noisy_params['IMX686'] = {
# '100':{'K':0.1366021, 'sigGs':0.6926457, 'sigGssig':0.002096},
# '6400':{'K':8.7425333, 'sigGs':14.303619546153575, 'sigGssig':0.0696716845864088},
# }
# if camera_type in cam_noisy_params:
# return cam_noisy_params[camera_type][iso]
# else:
# log(f'''Warning: we have not test the noisy parameters of camera "{camera_type}". Now we use NikonD850's parameters to test.''')
# return cam_noisy_params['IMX686']
def get_camera_noisy_params(camera_type=None):
cam_noisy_params = {}
cam_noisy_params['OS04J10'] = {#K_ISO=(0.01134,0.56113)
'K_ISO': (0.01143, 0.4127), 'ISOmin':100, 'ISOmax':1593, 'tmin':30, 'tmax':30, # exposure time(ms)
'Kmin':0.44193, 'Kmax':2.92427, 'lam':-0.094, 'q':2.442e-04, 'wp':4095, 'bl':256,
'sigRk':0.81696, 'sigRb':-2.69453, 'sigRsig':0.04015,
'sigTLk':0.82984, 'sigTLb':-0.76241, 'sigTLsig':0.06671,
'sigGsk':0.89423, 'sigGsb':-0.08060, 'sigGssig':0.04103,
'sigReadk':0.00772, 'sigReadb':0.65775, 'sigReadsig':0.34109,
'uReadk':0.00001, 'uReadb':0.00207, 'uReadsig':0.00059
}
cam_noisy_params['SC850AI'] = {
'Kmin':1.42030, 'Kmax':1.70373, 'lam':-0.035, 'q':1/959, 'wp':1023, 'bl':64,
'sigRk':0.85792, 'sigRb':-2.82338, 'sigRsig':0.01944,
'sigTLk':-0.15504, 'sigTLb':0.71196, 'sigTLsig':0.04500,
'sigGsk':0.60800, 'sigGsb':0.17009, 'sigGssig':0.02261,
'sigReadk':0.60940, 'sigReadb':0.16932, 'sigReadsig':0.02257,
'uReadk':0.00000, 'uReadb':-0.00292, 'uReadsig':0.00400
}
cam_noisy_params['SC450AI'] = { # ISO_3600_6052 (0.00065631, 0.02588613)
'Kmin':0.87071, 'Kmax':1.38576, 'lam':0.042, 'q':1/959, 'wp':1023, 'bl':64,
'sigRk':0.98972, 'sigRb':-2.69256, 'sigRsig':0.03678,
'sigTLk':0.75890, 'sigTLb':-0.51098, 'sigTLsig':0.04700,
'sigGsk':0.87135, 'sigGsb':-0.11635, 'sigGssig':0.04866,
'sigReadk':0.00044, 'sigReadb':0.30172, 'sigReadsig':0.12209,
'uReadk':-0.00005, 'uReadb':0.32066, 'uReadsig':0.01992
}
# new calibration (smaller)
cam_noisy_params['SC450'] = { # ISO_3600_6052 (0.00065631, 0.02588613)
'Kmin':0.87071, 'Kmax':1.38576, 'lam':0.042, 'q':1/959, 'wp':1023, 'bl':64,
'sigRk':0.97496, 'sigRb':-2.78752, 'sigRsig':0.02109,
'sigTLk':0.57693, 'sigTLb':-0.44032, 'sigTLsig':0.01515,
'sigGsk':0.88683, 'sigGsb':-0.26233, 'sigGssig':0.00695,
'sigReadk':0.00040, 'sigReadb':0.23393, 'sigReadsig':0.01396,
'uReadk':0.00000, 'uReadb':0.00076, 'uReadsig':0.00125
}
cam_noisy_params['NikonD850'] = {
'Kmin':1.2, 'Kmax':2.4828, 'lam':-0.26, 'q':1/(2**14), 'wp':16383, 'bl':512,
'sigTLk':0.906, 'sigTLb':-0.6754, 'sigTLsig':0.035165,
'sigRk':0.8322, 'sigRb':-2.3326, 'sigRsig':0.301333,
'sigGsk':0.906, 'sigGsb':-0.1754, 'sigGssig':0.035165,
}
cam_noisy_params['IMX686'] = { # ISO-640~6400
'Kmin':-0.19118, 'Kmax':2.16820, 'lam':0.102, 'q':1/(2**10), 'wp':1023, 'bl':64,
'sigTLk':0.85187, 'sigTLb':0.07991, 'sigTLsig':0.02921,
'sigRk':0.87611, 'sigRb':-2.11455, 'sigRsig':0.03274,
'sigGsk':0.85187, 'sigGsb':0.67991, 'sigGssig':0.02921,
}
cam_noisy_params['SonyA7S2_lowISO'] = {
'Kmin':-1.67214, 'Kmax':0.42228, 'lam':-0.026, 'q':1/(2**14), 'wp':16383, 'bl':512,
'sigRk':0.78782, 'sigRb':-0.34227, 'sigRsig':0.02832,
'sigTLk':0.74043, 'sigTLb':0.86182, 'sigTLsig':0.00712,
'sigGsk':0.82966, 'sigGsb':1.49343, 'sigGssig':0.00359,
'sigReadk':0.82879, 'sigReadb':1.50601, 'sigReadsig':0.00362,
'uReadk':0.01472, 'uReadb':0.01129, 'uReadsig':0.00034,
}
cam_noisy_params['SonyA7S2_highISO'] = {
'Kmin':0.64567, 'Kmax':2.51606, 'lam':-0.025, 'q':1/(2**14), 'wp':16383, 'bl':512,
'sigRk':0.62945, 'sigRb':-1.51040, 'sigRsig':0.02609,
'sigTLk':0.74901, 'sigTLb':-0.12348, 'sigTLsig':0.00638,
'sigGsk':0.82878, 'sigGsb':0.44162, 'sigGssig':0.00153,
'sigReadk':0.82645, 'sigReadb':0.45061, 'sigReadsig':0.00156,
'uReadk':0.00385, 'uReadb':0.00674, 'uReadsig':0.00039,
}
cam_noisy_params['CRVD'] = {
'Kmin':1.31339, 'Kmax':3.95448, 'lam':0.015, 'q':1/(2**12), 'wp':4095, 'bl':240,
'sigRk':0.93368, 'sigRb':-2.19692, 'sigRsig':0.02473,
'sigGsk':0.95387, 'sigGsb':0.01552, 'sigGssig':0.00855,
'sigTLk':0.95495, 'sigTLb':0.01618, 'sigTLsig':0.00790
}
if camera_type in cam_noisy_params:
return cam_noisy_params[camera_type]
else:
log(f'''Warning: we have not test the noisy parameters of camera "{camera_type}". Now we use NikonD850's parameters to test.''')
return cam_noisy_params['NikonD850']
def get_specific_noise_params(camera_type=None, iso='100'):
iso = str(iso)
cam_noisy_params = {}
cam_noisy_params['SonyA7S2'] = {
'50': {'Kmax': 0.047815, 'lam': 0.1474653, 'sigGs': 1.0164667, 'sigGssig': 0.005272454, 'sigTL': 0.70727646, 'sigTLsig': 0.004360543, 'sigR': 0.13997398, 'sigRsig': 0.0064381803, 'bias': 0, 'biassig': 0.010093017, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'64': {'Kmax': 0.0612032, 'lam': 0.13243394, 'sigGs': 1.0509665, 'sigGssig': 0.008081373, 'sigTL': 0.71535635, 'sigTLsig': 0.0056863446, 'sigR': 0.14346549, 'sigRsig': 0.006400559, 'bias': 0, 'biassig': 0.008690166, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'80': {'Kmax': 0.076504, 'lam': 0.1121489, 'sigGs': 1.180899, 'sigGssig': 0.011333668, 'sigTL': 0.7799473, 'sigTLsig': 0.009347968, 'sigR': 0.19540153, 'sigRsig': 0.008197397, 'bias': 0, 'biassig': 0.0107246125, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'100': {'Kmax': 0.09563, 'lam': 0.14875287, 'sigGs': 1.0067395, 'sigGssig': 0.0033682834, 'sigTL': 0.70181876, 'sigTLsig': 0.0037532174, 'sigR': 0.1391465, 'sigRsig': 0.006530218, 'bias': 0, 'biassig': 0.007235429, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'125': {'Kmax': 0.1195375, 'lam': 0.12904578, 'sigGs': 1.0279676, 'sigGssig': 0.007364685, 'sigTL': 0.6961967, 'sigTLsig': 0.0048687346, 'sigR': 0.14485553, 'sigRsig': 0.006731584, 'bias': 0, 'biassig': 0.008026363, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'160': {'Kmax': 0.153008, 'lam': 0.094135, 'sigGs': 1.1293099, 'sigGssig': 0.008340453, 'sigTL': 0.7258587, 'sigTLsig': 0.008032158, 'sigR': 0.19755602, 'sigRsig': 0.0082754735, 'bias': 0, 'biassig': 0.0101351, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'200': {'Kmax': 0.19126, 'lam': 0.07902429, 'sigGs': 1.2926387, 'sigGssig': 0.012171176, 'sigTL': 0.8117464, 'sigTLsig': 0.010250768, 'sigR': 0.22815849, 'sigRsig': 0.010726711, 'bias': 0, 'biassig': 0.011413908, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'250': {'Kmax': 0.239075, 'lam': 0.051688068, 'sigGs': 1.4345995, 'sigGssig': 0.01606571, 'sigTL': 0.8630922, 'sigTLsig': 0.013844714, 'sigR': 0.26271912, 'sigRsig': 0.0130637, 'bias': 0, 'biassig': 0.013569083, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'320': {'Kmax': 0.306016, 'lam': 0.040700804, 'sigGs': 1.7481371, 'sigGssig': 0.019626873, 'sigTL': 1.0334468, 'sigTLsig': 0.017629284, 'sigR': 0.3097104, 'sigRsig': 0.016202712, 'bias': 0, 'biassig': 0.017825918, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'400': {'Kmax': 0.38252, 'lam': 0.0222538, 'sigGs': 2.0595572, 'sigGssig': 0.024872316, 'sigTL': 1.1816813, 'sigTLsig': 0.02505812, 'sigR': 0.36209714, 'sigRsig': 0.01994737, 'bias': 0, 'biassig': 0.021005306, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'500': {'Kmax': 0.47815, 'lam': -0.0031342343, 'sigGs': 2.3956928, 'sigGssig': 0.030144656, 'sigTL': 1.31772, 'sigTLsig': 0.028629242, 'sigR': 0.42528257, 'sigRsig': 0.025104137, 'bias': 0, 'biassig': 0.02981831, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'640': {'Kmax': 0.612032, 'lam': 0.002566592, 'sigGs': 2.9662898, 'sigGssig': 0.045661453, 'sigTL': 1.6474211, 'sigTLsig': 0.04671843, 'sigR': 0.48839623, 'sigRsig': 0.031589635, 'bias': 0, 'biassig': 0.10000693, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'800': {'Kmax': 0.76504, 'lam': -0.008199721, 'sigGs': 3.5475867, 'sigGssig': 0.052318197, 'sigTL': 1.9346539, 'sigTLsig': 0.046128694, 'sigR': 0.5723769, 'sigRsig': 0.037824076, 'bias': 0, 'biassig': 0.025339302, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'1000': {'Kmax': 0.9563, 'lam': -0.021061005, 'sigGs': 4.2727833, 'sigGssig': 0.06972333, 'sigTL': 2.2795107, 'sigTLsig': 0.059203167, 'sigR': 0.6845563, 'sigRsig': 0.04879781, 'bias': 0, 'biassig': 0.027911892, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'1250': {'Kmax': 1.195375, 'lam': -0.032423194, 'sigGs': 5.177596, 'sigGssig': 0.092677385, 'sigTL': 2.708437, 'sigTLsig': 0.07622563, 'sigR': 0.8177013, 'sigRsig': 0.06162229, 'bias': 0, 'biassig': 0.03293372, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'1600': {'Kmax': 1.53008, 'lam': -0.0441045, 'sigGs': 6.29925, 'sigGssig': 0.1153261, 'sigTL': 3.2283993, 'sigTLsig': 0.09118158, 'sigR': 0.988786, 'sigRsig': 0.078567736, 'bias': 0, 'biassig': 0.03877672, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'2000': {'Kmax': 1.9126, 'lam': -0.012963797, 'sigGs': 2.653871, 'sigGssig': 0.015890995, 'sigTL': 1.4356787, 'sigTLsig': 0.02178686, 'sigR': 0.33124214, 'sigRsig': 0.018801652, 'bias': 0, 'biassig': 0.01570677, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'2500': {'Kmax': 2.39075, 'lam': -0.027097283, 'sigGs': 3.200225, 'sigGssig': 0.019307792, 'sigTL': 1.6897862, 'sigTLsig': 0.025873765, 'sigR': 0.38264316, 'sigRsig': 0.023769397, 'bias': 0, 'biassig': 0.018728448, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'3200': {'Kmax': 3.06016, 'lam': -0.034863412, 'sigGs': 3.9193838, 'sigGssig': 0.02649232, 'sigTL': 2.0417721, 'sigTLsig': 0.032873377, 'sigR': 0.44543457, 'sigRsig': 0.030114045, 'bias': 0, 'biassig': 0.021355819, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'4000': {'Kmax': 3.8252, 'lam': -0.043700505, 'sigGs': 4.8015847, 'sigGssig': 0.03781628, 'sigTL': 2.4629273, 'sigTLsig': 0.042401053, 'sigR': 0.52347374, 'sigRsig': 0.03929801, 'bias': 0, 'biassig': 0.026152484, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'5000': {'Kmax': 4.7815, 'lam': -0.053150143, 'sigGs': 5.8995814, 'sigGssig': 0.0625814, 'sigTL': 2.9761007, 'sigTLsig': 0.061326735, 'sigR': 0.6190265, 'sigRsig': 0.05335372, 'bias': 0, 'biassig': 0.058574405, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'6400': {'Kmax': 6.12032, 'lam': -0.07517104, 'sigGs': 7.1163535, 'sigGssig': 0.08435366, 'sigTL': 3.4502964, 'sigTLsig': 0.08226275, 'sigR': 0.7218788, 'sigRsig': 0.0642334, 'bias': 0, 'biassig': 0.059074216, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'8000': {'Kmax': 7.6504, 'lam': -0.08208357, 'sigGs': 8.916516, 'sigGssig': 0.12763213, 'sigTL': 4.269624, 'sigTLsig': 0.13381928, 'sigR': 0.87760293, 'sigRsig': 0.07389065, 'bias': 0, 'biassig': 0.084842026, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'10000': {'Kmax': 9.563, 'lam': -0.073289566, 'sigGs': 11.291476, 'sigGssig': 0.1639773, 'sigTL': 5.495318, 'sigTLsig': 0.16279395, 'sigR': 1.0522343, 'sigRsig': 0.094359785, 'bias': 0, 'biassig': 0.107438326, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'12800': {'Kmax': 12.24064, 'lam': -0.06495205, 'sigGs': 14.245901, 'sigGssig': 0.17283991, 'sigTL': 7.038261, 'sigTLsig': 0.18822834, 'sigR': 1.2749791, 'sigRsig': 0.120479785, 'bias': 0, 'biassig': 0.0944684, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'16000': {'Kmax': 15.3008, 'lam': -0.060692135, 'sigGs': 17.833515, 'sigGssig': 0.19809262, 'sigTL': 8.877547, 'sigTLsig': 0.23338738, 'sigR': 1.5559287, 'sigRsig': 0.15791349, 'bias': 0, 'biassig': 0.09725099, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'20000': {'Kmax': 19.126, 'lam': -0.060213074, 'sigGs': 22.084776, 'sigGssig': 0.21820943, 'sigTL': 11.002351, 'sigTLsig': 0.28806436, 'sigR': 1.8810822, 'sigRsig': 0.18937257, 'bias': 0, 'biassig': 0.4984733, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512},
'25600': {'Kmax': 24.48128, 'lam': -0.09089118, 'sigGs': 25.853043, 'sigGssig': 0.35371417, 'sigTL': 12.175712, 'sigTLsig': 0.4215717, 'sigR': 2.2760193, 'sigRsig': 0.2609267, 'bias': 0, 'biassig': 0.37568903, 'q': 6.103515625e-05, 'wp': 16383, 'bl': 512}
}
cam_noisy_params['NikonD850'] = {
'800':{
'Kmax':3.320, 'sigGs':4.858579, 'sigGssig':0.002096,
'sigTL':1.509532, 'lam':-0.26, 'sigR':0.263432,
'q':1/(2**14), 'wp':16383, 'bl':512,
'bias':np.array([0,0,0,0])
},
'1600':{
'Kmax':6.305, 'sigGs':8.695116, 'sigGssig':0.06967,
'sigTL':2.699073, 'lam':-0.26, 'sigR':0.449245,
'q':1/(2**14), 'wp':16383, 'bl':512,
'bias':np.array([0,0,0,0])
},
'3200':{
'Kmax':11.975, 'sigGs':15.514215, 'sigGssig':0.06967,
'sigTL':4.825994, 'lam':-0.26, 'sigR':0.766122,
'q':1/(2**14), 'wp':16383, 'bl':512,
'bias':np.array([0,0,0,0])
}
}
cam_noisy_params['IMX686'] = {
'100':{
'Kmax':0.083805, 'sigGs':0.6926457, 'sigGssig':0.002096,
'sigTL':0.67998, 'lam':0.10229621, 'sigR':0.23668,
'q':1/(2**10), 'wp':1023, 'bl':64,
'bias':np.array([0,0,0,0])
},
'6400':{
'Kmax':8.74253, 'sigGs':12.8901, 'sigGssig':0.06967,
'sigTL':12.8901, 'lam':0.10229621, 'sigR':0,
'q':1/(2**10), 'wp':1023, 'bl':64,
'bias':np.array([-0.08113494,-0.04906388,-0.9408157,-1.2048522])
}
}
if camera_type in cam_noisy_params:
return cam_noisy_params[camera_type][iso]
else:
# log(f'''Warning: we have not test the noisy parameters of camera "{camera_type}".''')
return None
# 取最大对应相机的最大ISO生成噪声参数
def sample_params_max(camera_type='NikonD850', ratio=None, iso=None):
# 获取已经测算好的相机噪声参数
params = None
if iso is not None:
params = get_specific_noise_params(camera_type=camera_type, iso=iso)
if params is None:
if camera_type in Dual_ISO_Cameras:
choice = np.random.randint(2)
camera_type += '_lowISO' if choice<1 else '_highISO'
params = get_camera_noisy_params(camera_type=camera_type)
# 根据最小二乘法得到的噪声参数回归模型采样噪声参数
bias = 0
if 'K_ISO' in params and iso is not None:
K_ISO = params['K_ISO']
K = np.array(K_ISO[0] * iso + K_ISO[1]) * (1 + np.random.uniform(low=-0.02, high=+0.02))
log_K = np.log(K)
else:
log_K = params['Kmax'] * (1 + np.random.uniform(low=-0.02, high=+0.02)) # 增加一些扰动,以防测的不准
K = np.exp(log_K)
mu_TL = params['sigTLk']*log_K + params['sigTLb']
mu_R = params['sigRk']*log_K + params['sigRb']
mu_Gs = params['sigGsk']*log_K + params['sigGsb'] if 'sigGsk' in params else 2**(-14)
# 去掉log
sigTL = np.exp(mu_TL)
sigR = np.exp(mu_R)
sigGs = np.exp(np.random.normal(loc=mu_Gs, scale=params['sigGssig']) if 'sigGssig' in params else mu_Gs)
else:
K = params['Kmax'] * (1 + np.random.uniform(low=-0.01, high=+0.01)) # 增加一些扰动,以防测的不准
sigGs = np.random.normal(loc=params['sigGs'], scale=params['sigGssig']) if 'sigGssig' in params else params['sigGs']
sigTL = np.random.normal(loc=params['sigTL'], scale=params['sigTLsig']) if 'sigTLsig' in params else params['sigTL']
sigR = np.random.normal(loc=params['sigR'], scale=params['sigRsig']) if 'sigRsig' in params else params['sigR']
bias = params['bias']
wp = params['wp']
bl = params['bl']
lam = params['lam']
q = params['q']
if ratio is None:
if 'SonyA7S2' in camera_type:
ratio = np.random.uniform(low=100, high=300)
else:
log_ratio = np.random.uniform(low=0, high=2.08)
ratio = np.exp(log_ratio)
return {'K':K, 'sigTL':sigTL, 'sigR':sigR, 'sigGs':sigGs, 'bias':bias,
'lam':lam, 'q':q, 'ratio':ratio, 'wp':wp, 'bl':bl}
# 噪声参数采样
def sample_params(camera_type='NikonD850', ln_ratio=False):
choice = 1
Dual_ISO_Cameras = ['SonyA7S2']
if camera_type in Dual_ISO_Cameras:
choice = np.random.randint(2)
camera_type += '_lowISO' if choice<1 else '_highISO'
# 获取已经测算好的相机噪声参数
params = get_camera_noisy_params(camera_type=camera_type)
wp = params['wp']
bl = params['bl']
lam = params['lam']
q = params['q']
Point_ISO_Cameras = ['CRVD', 'BM3D']
if camera_type in Point_ISO_Cameras:
if camera_type == 'CRVD':
iso_list = [1600,3200,6400,12800,25600]
a_list = np.array([3.513262,6.955588,13.486051,26.585953,52.032536])
b_list = np.array([11.917691,38.117816,130.818508,484.539790,1819.818657])
bias_points = np.array([-1.12660, -1.69546, -3.25935, -6.68111, -12.66876])
K_points = np.log(a_list)
Gs_points = np.log(np.sqrt(b_list))
choice = np.random.randint(5)
# 取点噪声参数
log_K = K_points[choice]
K = a_list[choice]
mu_TL = params['sigTLk']*log_K + params['sigTLb'] if 'sigTLk' in params else 0
mu_R = params['sigRk']*log_K + params['sigRb'] if 'sigRk' in params else 0
mu_Gs = Gs_points[choice]
bias = bias_points[choice]
else:
# 根据最小二乘法得到的噪声参数回归模型采样噪声参数
bias = 0
log_K = np.random.uniform(low=params['Kmin'], high=params['Kmax'])
K = np.exp(log_K)
mu_TL = params['sigTLk']*log_K + params['sigTLb'] if 'sigTLk' in params else q
mu_R = params['sigRk']*log_K + params['sigRb'] if 'sigRk' in params else q
mu_Gs = params['sigGsk']*log_K + params['sigGsb'] if 'sigGsk' in params else q
mu_bias = params['uReadk']*log_K + params['uReadb']
log_sigTL = np.random.normal(loc=mu_TL, scale=params['sigTLsig']) if 'sigTLk' in params else 0
log_sigR = np.random.normal(loc=mu_R, scale=params['sigRsig']) if 'sigRk' in params else 0
log_sigGs = np.random.normal(loc=mu_Gs, scale=params['sigGssig']) if 'sigGsk' in params else q
log_bias = np.random.normal(loc=mu_bias, scale=params['uReadsig']) if 'uReadk' in params else 0
# 去掉log
sigTL = np.exp(log_sigTL)
sigR = np.exp(log_sigR)
sigGs = np.exp(log_sigGs)
bias = np.exp(log_bias)
# 模拟曝光衰减的系数, ln_ratio模式会照顾弱噪声场景,更有通用性
if ln_ratio:
high = 1 if 'CRVD' in camera_type else 5
log_ratio = np.random.uniform(low=-0.01, high=high)
ratio = np.exp(log_ratio) # np.random.uniform(low=1, high=200) if choice else np.exp(log_ratio)
else:
ratio = np.random.uniform(low=100, high=300)
return {'K':K, 'sigTL':sigTL, 'sigR':sigR, 'sigGs':sigGs, 'bias':bias,
'lam':lam, 'q':q, 'ratio':ratio, 'wp':wp, 'bl':bl}
def get_aug_param_torch(data, b=8, command='augv5', numpy=False, camera_type='SonyA7S2'):
aug_r, aug_g, aug_b = torch.zeros(b), torch.zeros(b), torch.zeros(b)
r = np.random.randint(2) * 0.25 + 0.25
u = r
if np.random.randint(4):
if 'augv5' in command:
# v5,依照相机白平衡经验参数增强,激进派作风
rgb_gain, red_gain, blue_gain = random_gains(camera_type)
rgb_gain = 1 / rgb_gain
rg = data["wb"][:, 0] / red_gain[0]
bg = data["wb"][:, 2] / blue_gain[0]
aug_g = torch.rand(b) * r + rgb_gain[0] - 0.9
aug_r = torch.rand(b) * r + rg * (1+aug_g) - 1.1
aug_b = torch.rand(b) * r + bg * (1+aug_g) - 1.1
elif 'augv2' in command:
# v2,相信原数据颜色的准确性,保守派作风
aug_g = torch.clamp(torch.randn(b) * r, 0, 4*u)
aug_r = torch.clamp((1+torch.randn(b)*r) * (1+aug_g) - 1, 0, 4*u)
aug_b = torch.clamp((1+torch.randn(b)*r) * (1+aug_g) - 1, 0, 4*u)
# 保证非负
daug, _ = torch.min(torch.stack((aug_r, aug_g, aug_b)), dim=0)
daug[daug>0] = 0
aug_r = (1+aug_r) / (1+daug) - 1
aug_g = (1+aug_g) / (1+daug) - 1
aug_b = (1+aug_b) / (1+daug) - 1
if numpy:
aug_r = np.squeeze(aug_r.numpy())
aug_g = np.squeeze(aug_g.numpy())
aug_b = np.squeeze(aug_b.numpy())
return aug_r, aug_g, aug_b
def raw_wb_aug(noisy, gt, aug_wb=None, camera_type='SonyA7S2', ratio=1, ori=True, iso=None):
# [c, h, w]
p = get_specific_noise_params(camera_type=camera_type, iso=iso)
if p is None:
assert camera_type == 'SonyA7S2'
camera_type += '_lowISO' if iso<=1600 else '_highISO'
p = get_camera_noisy_params(camera_type=camera_type)
# 根据最小二乘法得到的噪声参数回归模型采样噪声参数
p['K'] = 0.0009546 * iso * (1 + np.random.uniform(low=-0.01, high=+0.01)) - 0.00193
log_K = np.log(p['K'])
mu_Gs = p['sigGsk']*log_K + p['sigGsb']
p['sigGs'] = np.exp(np.random.normal(loc=mu_Gs, scale=p['sigGssig']))
else:
p['K'] = p['Kmax'] * (1 + np.random.uniform(low=-0.01, high=+0.01)) # 增加一些扰动,以防测的不准
p['sigGs'] = np.random.normal(loc=p['sigGs'], scale=p['sigGssig']) if 'sigGssig' in p else p['sigGs']
if aug_wb is not None:
# 默认pattern为RGGB!(通道rgbg排列)
gt = gt * (p['wp'] - p['bl']) / ratio
noisy = noisy * (p['wp'] - p['bl'])
# 补噪声
daug = -np.minimum(np.min(aug_wb), 0)
# aug_wb = aug_wb + daug
if daug == 0:
# 只有增益的话很好处理,叠加泊松分布就行
dy = gt * aug_wb.reshape(-1,1,1) # 我考虑过这里dy和dn要不要量化一下,感觉不量化对多样性更友好
dn = np.random.poisson(dy/p['K']).astype(np.float32) * p['K']
else:
# BiSNA,折中方案,不好讲故事,弃疗了
raise NotImplementedError
# 存在减益的话就很麻烦,需要考虑read noise并且补齐分布
scale = 1 - daug
# 要保证dyn是非负的
aug_wb_new = aug_wb + daug
dy = gt * aug_wb.reshape(-1,1,1)
dyn = gt * aug_wb_new.reshape(-1,1,1)
# 先通过缩放减小噪声图
noisy *= scale
# 补齐单个照片的读噪声
dn_read = np.random.randn(*gt.shape).astype(np.float32) * p['sigGs'] * np.sqrt(1-scale**2)
# 补齐由于除法导致的分布变化,恢复shot noise应有的分布(不完全相等)
scale_sigma = scale - scale**2
dn_shot = np.random.poisson(scale_sigma * gt/p['K']).astype(np.float32)*p['K'] - gt * scale_sigma
# 叠加泊松分布
dn_aug = np.random.poisson(dyn/p['K']).astype(np.float32) * p['K']
dn = dn_read + dn_shot + dn_aug
gt = np.clip((gt + dy)*ratio, 0, (p['wp'] - p['bl']))
noisy = np.clip(noisy + dn, -p['bl'], (p['wp'] - p['bl']))
gt /= (p['wp'] - p['bl'])
noisy /= (p['wp'] - p['bl'])
if ori is False:
noisy *= ratio
return noisy.astype(np.float32), gt.astype(np.float32)
def raw_wb_aug_torch(noisy, gt, aug_wb=None, camera_type='IMX686', ratio=1, ori=True, iso=None, ratiofix=False):
p = get_specific_noise_params(camera_type=camera_type, iso=iso)
if p is None:
assert camera_type == 'SonyA7S2'
camera_type += '_lowISO' if iso<=1600 else '_highISO'
p = get_camera_noisy_params(camera_type=camera_type)
# 根据最小二乘法得到的噪声参数回归模型采样噪声参数
p['K'] = 0.0009546 * iso * (1 + np.random.uniform(low=-0.01, high=+0.01)) - 0.00193
log_K = np.log(p['K'])
mu_Gs = p['sigGsk']*log_K + p['sigGsb']
p['sigGs'] = np.exp(np.random.normal(loc=mu_Gs, scale=p['sigGssig']))
else:
p['K'] = p['Kmax'] * (1 + np.random.uniform(low=-0.01, high=+0.01)) # 增加一些扰动,以防测的不准
p['sigGs'] = np.random.normal(loc=p['sigGs'], scale=p['sigGssig']) if 'sigGssig' in p else p['sigGs']
if aug_wb is not None:
# 默认pattern为RGGB!(通道rgbg排列)
gt = gt * (p['wp'] - p['bl']) / ratio
noisy = noisy * (p['wp'] - p['bl'])
# 补噪声
daug = -np.minimum(np.min(aug_wb), 0)
daug = torch.from_numpy(np.array(daug)).to(noisy.device)
aug_wb = torch.from_numpy(aug_wb).to(noisy.device)
dy = gt * aug_wb.reshape(-1,1,1) # 我考虑过这里dy和dn要不要量化一下,感觉不量化对多样性更友好
if daug == 0:
# 只有增益的话很好处理,叠加泊松分布就行
dn = tdist.Poisson(dy/p['K']).sample() * p['K']
else:
# BiSNA,折中方案,不好讲故事,弃疗了
warnings.warn('You are using BiSNA!!!')
raise NotImplementedError
# 存在减益的话就很麻烦,需要考虑read noise并且补齐分布
scale = 1 - daug
# 要保证dyn是非负的
aug_wb_new = aug_wb + daug
dyn = gt * aug_wb_new.reshape(-1,1,1)
# 先通过缩放减小噪声图
noisy *= scale
# 补齐单个照片的读噪声
dn_read = tdist.Normal(0, p['sigGs']).sample() * torch.sqrt(1-scale**2)
# 补齐由于除法导致的分布变化,恢复shot noise应有的分布(不完全相等)
scale_sigma = scale - scale**2
dn_shot = tdist.Poisson(scale_sigma * gt/p['K']).sample() *p['K'] - gt * scale_sigma
# 叠加泊松分布
dn_aug = tdist.Poisson(dyn/p['K']).sample() * p['K']
dn = dn_read + dn_shot + dn_aug
if ratiofix:
ratio = ratio / (1 + daug)
gt = torch.clamp((gt + dy)*ratio, 0, (p['wp'] - p['bl']))
noisy = torch.clamp(noisy + dn, -p['bl'], (p['wp'] - p['bl']))
gt /= (p['wp'] - p['bl'])
noisy /= (p['wp'] - p['bl'])
if ori is False:
noisy *= ratio
return noisy, gt
def SNA_torch(gt, aug_wb, camera_type='IMX686', ratio=1, black_lr=False, ori=True, iso=None):
p = get_specific_noise_params(camera_type=camera_type, iso=iso)
if p is None:
assert camera_type == 'SonyA7S2'
camera_type += '_lowISO' if iso<=1600 else '_highISO'
p = get_camera_noisy_params(camera_type=camera_type)
# 根据最小二乘法得到的噪声参数回归模型采样噪声参数
p['K'] = 0.0009546 * iso * (1 + np.random.uniform(low=-0.01, high=+0.01)) - 0.00193
else:
p['K'] = p['Kmax'] * (1 + np.random.uniform(low=-0.01, high=+0.01)) # 增加一些扰动,以防测的不准
# 默认pattern为RGGB!(通道rgbg排列)
gt = gt * (p['wp'] - p['bl']) / ratio
# 补噪声
aug_wb = torch.from_numpy(aug_wb).to(gt.device)
dy = gt * aug_wb.reshape(-1,1,1) # 我考虑过这里dy和dn要不要量化一下,感觉不量化对多样性更友好
# 只有增益的话很好处理,叠加泊松分布就行
dn = tdist.Poisson(dy/p['K']).sample() * p['K']
# 贴黑图的,所以抛去gt中多算的一份泊松分布
if black_lr: dy = dy - gt
dy = dy * ratio / (p['wp'] - p['bl'])
dn = dn / (p['wp'] - p['bl'])
if ori is False:
dn *= ratio
return dn, dy
# @ fn_timer
def generate_noisy_obs(y, camera_type=None, noise_code='p', param=None, MultiFrameMean=1, ori=False, clip=False):
p = param
y = y * (p['wp'] - p['bl'])
# p['ratio'] = 1/p['ratio'] # 临时行为,为了快速实现MFM
y = y / p['ratio']
MFM = MultiFrameMean ** 0.5
use_R = True if 'r' in noise_code.lower() else False
use_Q = True if 'q' in noise_code.lower() else False
use_TL = True if 'g' in noise_code.lower() else False
use_P = True if 'p' in noise_code.lower() else False
use_D = True if 'd' in noise_code.lower() else False
use_black = True if 'b' in noise_code.lower() else False
if use_P: # 使用泊松噪声作为shot noisy
noisy_shot = np.random.poisson(MFM*y/p['K']).astype(np.float32) * p['K'] / MFM
else: # 不考虑shot noisy
noisy_shot = y + np.random.randn(*y.shape).astype(np.float32) * np.sqrt(np.maximum(y/p['K'], 1e-10)) * p['K'] / MFM
if not use_black:
if use_TL: # 使用TL噪声作为read noisy
noisy_read = stats.tukeylambda.rvs(p['lam'], scale=p['sigTL']/MFM, size=y.shape).astype(np.float32)
else: # 使用高斯噪声作为read noisy
noisy_read = stats.norm.rvs(scale=p['sigGs']/MFM, size=y.shape).astype(np.float32)
# 行噪声需要使用行的维度h,[1,c,h,w]所以-2是h
noisy_row = np.random.randn(y.shape[-3], y.shape[-2], 1).astype(np.float32) * p['sigR']/MFM if use_R else 0
noisy_q = np.random.uniform(low=-0.5, high=0.5, size=y.shape) if use_Q else 0
noisy_bias = p['bias'].reshape(-1,1,1) if use_D else 0
else:
noisy_read = 0
noisy_row = 0
noisy_q = 0
noisy_bias = 0
# 归一化回[0, 1]
z = (noisy_shot + noisy_read + noisy_row + noisy_q + noisy_bias) / (p['wp'] - p['bl'])
# 模拟实际情况
z = np.clip(z, -p['bl']/p['wp'], 1) if not clip else np.clip(z, 0, 1)
if ori is False:
z = z * p['ratio']
return z.astype(np.float32)
# @fn_timer
def generate_noisy_torch(y, camera_type=None, noise_code='p', param=None, MultiFrameMean=1, ori=False, clip=False):
p = param
y = y * (p['wp'] - p['bl'])
# p['ratio'] = 1/p['ratio'] # 临时行为,为了快速实现MFM
y = y / p['ratio']
MFM = MultiFrameMean ** 0.5
use_R = True if 'r' in noise_code.lower() else False
use_Q = True if 'q' in noise_code.lower() else False
use_TL = True if 'g' in noise_code.lower() else False
use_P = True if 'p' in noise_code.lower() else False
use_D = True if 'd' in noise_code.lower() else False
use_black = True if 'b' in noise_code.lower() else False
if use_P: # 使用泊松噪声作为shot noisy
noisy_shot = tdist.Poisson(MFM*y/p['K']).sample() * p['K'] / MFM
else: # 不考虑shot noisy
noisy_shot = tdist.Normal(y).sample() * torch.sqrt(torch.max(y/p['K'], 1e-10)) * p['K'] / MFM
if not use_black:
if use_TL: # 使用TL噪声作为read noisy
raise NotImplementedError
# noisy_read = stats.tukeylambda.rvs(p['lam'], scale=p['sigTL'], size=y.shape).astype(np.float32)
else: # 使用高斯噪声作为read noisy
noisy_read = tdist.Normal(loc=torch.zeros_like(y), scale=p['sigGs']/MFM).sample()
else:
noisy_read = 0
# 使用行噪声
noisy_row = torch.randn(y.shape[-3], y.shape[-2], 1, device=y.device) * p['sigR'] / MFM if use_R else 0
noisy_q = (torch.rand(y.shape, device=y.device) - 0.5) * p['q'] * (p['wp'] - p['bl']) if use_Q else 0
noisy_bias = torch.from_numpy(p['bias'].reshape(-1,1,1)) if use_D else 0
# 归一化回[0, 1]
z = (noisy_shot + noisy_read + noisy_row + noisy_q + noisy_bias) / (p['wp'] - p['bl'])
# 模拟实际情况
z = torch.clamp(z, -p['bl']/p['wp'], 1) if not clip else torch.clamp(z, 0, 1)
# ori_brightness
if ori is False:
z = z * p['ratio']
return z
class HighBitRecovery:
def __init__(self, camera_type='IMX686', noise_code='prq', param=None,
perturb=True, factor=6, float=True):
self.camera_type = camera_type
self.noise_code = noise_code
self.param = param
self.perturb = perturb
self.factor = factor
self.float = float
self.lut = {}
def get_lut(self, iso_list, blc_mean=None):
for iso in iso_list:
if blc_mean is None:
bias = 0
else:
bias = np.mean(blc_mean[iso])
if self.perturb:
sigma_t = 0.1
bias += np.random.randn() * sigma_t
self.lut[iso] = self.HB2LB_LUT(iso, bias)
def HB2LB_LUT(self, iso, bias=0, param=None):
# 标记LUT区间
lut_info = {}
# 获得标定的噪声参数
p = sample_params_max(self.camera_type, iso=iso) if param is None else param
lut_info['param'] = p
# 选择一种分布,依照该分布映射到HighBit
if 'g' in self.noise_code.lower():
dist = stats.tukeylambda(p['lam'], loc=bias, scale=p['sigTL'])
sigma = p['sigTL']
lut_info['dist'] = dist
else:
dist = stats.norm(loc=bias, scale=p['sigGs'])
sigma = p['sigGs']
lut_info['dist'] = dist
# 寻址范围为[-6σ,6σ],离群点不做映射恢复
low = max(int(-sigma*self.factor + bias), -p['bl']+1)
high = int(sigma*self.factor + bias)
for x in range(low, high):
lut_info[x] = {
# 累积概率函数的起点
'cdf': dist.cdf(x-0.5),
# 累积概率函数的变化范围
'range': dist.cdf(x+0.5) - dist.cdf(x-0.5),
}
lut_info['low'] = low
lut_info['high'] = high
lut_info['bias'] = bias
lut_info['sigma'] = sigma
return lut_info
def map(self, data, iso=6400, norm=True): # 将LB图像映射成HB图像
p = self.lut[iso]['param']
if np.max(data) <= 1: data = data * (p['wp'] - p['bl'])
data_float = data.copy()
data = np.round(data_float)
if self.float:
delta = data_float - data
rand = np.random.uniform(0, 1, size=data.shape)
# 快速版:寻址范围为[-6σ,6σ],离群点不做映射恢复
for x in range(self.lut[iso]['low'], self.lut[iso]['high']):
keys = (data==x)
cdf = self.lut[iso][x]['cdf']
r = self.lut[iso][x]['range']
# 根据ppf反推分位点
data[keys] = self.lut[iso]['dist'].ppf(cdf + rand[keys] * r)
if self.float:
data = data + delta
if norm:
data = data / (p['wp'] - p['bl'])
else:
data = data + p['bl']
return data
class ELDEvalDataset(torch.utils.data.Dataset):
# 初始化函数,传入参数:basedir(基础路径),camera_suffix(相机后缀),scenes(场景),img_ids(图像id)
def __init__(self, basedir, camera_suffix=('NikonD850','.nef'), scenes=None, img_ids=None):
super(ELDEvalDataset, self).__init__()
self.basedir = basedir
self.camera_suffix = camera_suffix # ('Canon', '.CR2')
self.scenes = scenes
self.img_ids = img_ids
# self.input_dict = {}
# self.target_dict = {}
# 获取每一个图像的数据
def __getitem__(self, i):
camera, suffix = self.camera_suffix
scene_id = i // len(self.img_ids)
img_id = i % len(self.img_ids)
scene ='scene-{}'.format(self.scenes[scene_id])
datadir = os.path.join(self.basedir, camera, scene)
input_path = os.path.join(datadir, 'IMG_{:04d}{}'.format(self.img_ids[img_id], suffix))
gt_ids = np.array([1, 6, 11, 16])
ind = np.argmin(np.abs(self.img_ids[img_id] - gt_ids))
target_path = os.path.join(datadir, 'IMG_{:04d}{}'.format(gt_ids[ind], suffix))
iso, expo = metainfo(target_path)
target_expo = iso * expo
iso, expo = metainfo(input_path)
ratio = target_expo / (iso * expo)
with rawpy.imread(input_path) as raw:
input = pack_raw_bayer(raw) * ratio
with rawpy.imread(target_path) as raw:
target = pack_raw_bayer(raw)
input = np.maximum(np.minimum(input, 1.0), 0)
target = np.maximum(np.minimum(target, 1.0), 0)
input = np.ascontiguousarray(input)
target = np.ascontiguousarray(target)
data = {'input': input, 'target': target, 'fn':input_path, 'rawpath': target_path}
return data
# 获取数据的长度
def __len__(self):
return len(self.scenes) * len(self.img_ids)
if __name__ == '__main__':
path = 'F:/datasets/ELD/SonyA7S2/scene-8'
files = [os.path.join(path, name) for name in os.listdir(path) if '.ARW' in name]
for name in files:
print(name)
raw = rawpy.imread(name)
img = raw.raw_image_visible.astype(np.float32)[np.newaxis,:,:]
# img = img[:, 1000:1500, 2200:2700]
fig = plt.figure(figsize=(16,10))
img = np.clip((img-512) / (16383-512), 0, 1)
p = sample_params_max(camera_type='SonyA7S2', ratio=100, iso=1600)
noisy = generate_noisy_obs(img,camera_type='SonyA7S2', param=p)
# refer_path = path+'\\'+'DSC02750.ARW'
# raw_refer = rawpy.imread(refer_path)
# print(np.min(raw_refer.raw_image_visible), np.max(raw_refer.raw_image_visible), np.mean(raw_refer.raw_image_visible))
# raw_refer.raw_image_visible[:,:] = np.clip((raw_refer.raw_image_visible.astype(np.float32)-512) / (16383-512)*200, 0, 1)*16383
# print(np.min(raw_refer.raw_image_visible), np.max(raw_refer.raw_image_visible), np.mean(raw_refer.raw_image_visible))
# out1 = raw_refer.postprocess(use_camera_wb=True, no_auto_bright=True)
# print(np.min(out1), np.max(out1), np.mean(out1))
# plt.imsave('real.png', out1)
# plt.imshow(out1)
# plt.show()
raw.raw_image_visible[:,:] = noisy[0,:,:]*16383
out = raw.postprocess(use_camera_wb=True)
plt.imshow(out)
plt.show()
plt.imsave('gen.png', out)
print('test')
print("")