"""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("")