File size: 10,713 Bytes
0e07d71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
from .utils import *

def read_wb_ccm(raw):
    wb = np.array(raw.camera_whitebalance) 
    wb /= wb[1]
    wb = wb.astype(np.float32)
    ccm = raw.color_matrix[:3, :3].astype(np.float32)
    if ccm[0,0] == 0:
        ccm = np.eye(3, dtype=np.float32)
    return wb, ccm

def get_ISO_ExposureTime(filepath):
    # 不限于RAW,RGB图片也适用
    raw_file = open(filepath, 'rb')
    exif_file = exifread.process_file(raw_file, details=False, strict=True)
    # 获取曝光时间
    if 'EXIF ExposureTime' in exif_file:
        exposure_str = exif_file['EXIF ExposureTime'].printable
    else:
        exposure_str = exif_file['Image ExposureTime'].printable
    if '/' in exposure_str:
        fenmu = float(exposure_str.split('/')[0])
        fenzi = float(exposure_str.split('/')[-1])
        exposure = fenmu / fenzi
    else:
        exposure = float(exposure_str)
    # 获取ISO
    if 'EXIF ISOSpeedRatings' in exif_file:
        ISO_str = exif_file['EXIF ISOSpeedRatings'].printable
    else:
        ISO_str = exif_file['Image ISOSpeedRatings'].printable
    if '/' in ISO_str:
        fenmu = float(ISO_str.split('/')[0])
        fenzi = float(ISO_str.split('/')[-1])
        ISO = fenmu / fenzi
    else:
        ISO = float(ISO_str)
    info = {'ISO':int(ISO), 'ExposureTime':exposure, 'name':filepath.split('/')[-1]}
    return info

def metainfo(rawpath):
    with open(rawpath, 'rb') as f:
        tags = exifread.process_file(f)
        _, suffix = os.path.splitext(os.path.basename(rawpath))

        if suffix == '.dng':
            expo = eval(str(tags['Image ExposureTime']))
            iso = eval(str(tags['Image ISOSpeedRatings']))
        else:
            expo = eval(str(tags['EXIF ExposureTime']))
            iso = eval(str(tags['EXIF ISOSpeedRatings']))

        # print('ISO: {}, ExposureTime: {}'.format(iso, expo))
    return iso, expo

# Yuzhi Wang's ISP
def bayer2rggb(bayer):
    H, W = bayer.shape
    return bayer.reshape(H//2, 2, W//2, 2).transpose(0, 2, 1, 3).reshape(H//2, W//2, 4)

def rggb2bayer(rggb):
    H, W, _ = rggb.shape
    return rggb.reshape(H, W, 2, 2).transpose(0, 2, 1, 3).reshape(H*2, W*2)

def bayer2rggbs(bayers):
    H, W = bayers.shape[-2:]
    return bayers.reshape(-1, H//2, 2, W//2, 2).permute(0, 1, 3, 2, 4).reshape(-1, H//2, W//2, 4)

def rggb2bayers(rggbs):
    H, W, _ = rggbs.shape[-3:]
    return rggbs.reshape(-1, H, W, 2, 2).permute(0, 1, 3, 2, 4).reshape(-1, H*2, W*2)

def bayer2rows(bayer):
    # 分行
    H, W = bayer.shape
    return np.stack((bayer[0:H:2], bayer[1:H:2]))

def bayer2gray(raw):
    # 相当于双线性插值的bayer2gray
    kernel = np.array([[1,2,1],[2,4,2],[1,2,1]], np.float32) / 16.
    gray = cv2.filter2D(raw, -1, kernel, borderType=cv2.BORDER_REFLECT)
    return gray

def rows2bayer(rows):
    c, H, W = rows.shape
    bayer = np.empty((H*2, W))
    bayer[0:H*2:2] = rows[0]
    bayer[1:H*2:2] = rows[1]
    return bayer

# Kaixuan Wei's ISP
def raw2bayer(raw, wp=1023, bl=64, norm=True, clip=False, bias=np.array([0,0,0,0])):
    raw = raw.astype(np.float32)
    H, W = raw.shape
    out = np.stack((raw[0:H:2, 0:W:2], #RGBG
                    raw[0:H:2, 1:W:2],
                    raw[1:H:2, 1:W:2],
                    raw[1:H:2, 0:W:2]), axis=0).astype(np.float32) 
    if norm:
        bl = bias + bl
        bl = bl.reshape(4, 1, 1) 
        out = (out - bl) / (wp - bl)
    if clip: out = np.clip(out, 0, 1)
    return out.astype(np.float32) 

def bayer2raw(packed_raw, wp=16383, bl=512):
    if torch.is_tensor(packed_raw):
        packed_raw = packed_raw.detach()
        packed_raw = packed_raw[0].cpu().float().numpy()
    packed_raw = np.clip(packed_raw, 0, 1)
    packed_raw = packed_raw * (wp - bl) + bl
    C, H, W = packed_raw.shape
    H *= 2
    W *= 2
    raw = np.empty((H, W), dtype=np.uint16)
    raw[0:H:2, 0:W:2] = packed_raw[0, :,:]
    raw[0:H:2, 1:W:2] = packed_raw[1, :,:]
    raw[1:H:2, 1:W:2] = packed_raw[2, :,:]
    raw[1:H:2, 0:W:2] = packed_raw[3, :,:]
    return raw

# Hansen Feng's ISP
def repair_bad_pixels(raw, bad_points, method='median'):
    fixed_raw = bayer2rggb(raw)
    for i in range(4):
        fixed_raw[:,:,i] = cv2.medianBlur(fixed_raw[:,:,i],3)
    fixed_raw = rggb2bayer(fixed_raw)
    # raw = (1-bpc_map) * raw + bpc_map * fixed_raw
    for p in bad_points:
        raw[p[0],p[1]] = fixed_raw[p[0],p[1]]
    return raw

def SimpleISP(raw, bl=0, wp=1, wb=[2,1,1,2], gamma=2.2):
    # rggb2RGB (SimpleISP)
    raw = (raw.astype(np.float32) - bl) / (wp-bl)
    wb = np.array(wb)
    raw = raw * wb.reshape(1,1,-1)
    raw = raw.clip(0, 1)[:,:,(0,1,3)]
    raw = raw ** (1/gamma)
    return raw

def FastISP(img4c, wb=None, ccm=None, gamma=2.2, low_mem=True):
    # rgbg2RGB (FastISP)
    if torch.is_tensor(img4c):
        img4c = img4c[0].detach().cpu().numpy()
    h,w = img4c.shape[:2]
    H = h * 2
    W = w * 2
    raw = np.zeros((H,W), np.float32)
    red_gain = wb[0] if wb is not None else 2
    blue_gain = wb[2] if wb is not None else 2
    raw[0:H:2,0:W:2] = img4c[:,:,0] * red_gain # R
    raw[0:H:2,1:W:2] = img4c[:,:,1] # G1
    raw[1:H:2,0:W:2] = img4c[:,:,2] # G2
    raw[1:H:2,1:W:2] = img4c[:,:,3] * blue_gain # B
    raw = np.clip(raw, 0, 1)
    white_point = 16383
    raw = raw * white_point
    img = cv2.cvtColor(raw.astype(np.uint16), cv2.COLOR_BAYER_BG2RGB_EA) / white_point
    if ccm is None: ccm = np.eye(3, dtype=np.float32)
    img = img[:, :, None, :].astype(np.float32)
    ccm = ccm[None, None, :, :].astype(np.float32)
    if low_mem:
        n = 8
        h = img.shape[0] // n
        img_ccm = img.copy()
        img = img[:,:,0]
        for i in range(n):
            img[h*i:h*(i+1)] = np.sum(img_ccm[h*i:h*(i+1)] * ccm, axis=-1)
        img[h*n-h:] = np.sum(img_ccm[h*n-h:] * ccm, axis=-1)
    else:
        img = np.sum(img * ccm, axis=-1)
    img = np.clip(img, 0, 1) ** (1/gamma)
    return img

def raw2rgb_rawpy(packed_raw, wb=None, ccm=None, raw=None):
    """Raw2RGB pipeline (rawpy postprocess version)"""
    if raw is None:
        if packed_raw.shape[-2] > 1500:
            raw = rawpy.imread('templet.dng')
            wp = 1023
            bl = 64
        else:
            raw = rawpy.imread('templet.ARW')
            wp = 16383
            bl = 512
    if wb is None:
        wb = np.array(raw.camera_whitebalance) 
        wb /= wb[1]
    wb = list(wb)
    if ccm is None:
        try:
            ccm = raw.rgb_camera_matrix[:3, :3]
        except:
            warnings.warn("You have no Wei Kaixuan's customized rawpy, you can't get right ccm of SonyA7S2...")
            ccm = raw.color_matrix[:3, :3]
    elif np.max(np.abs(ccm - np.identity(3))) == 0:
        ccm = np.array([[ 1.9712269,-0.6789218,-0.29230508],
                    [-0.29104823,1.748401,-0.45735288],
                    [ 0.02051281,-0.5380369,1.5175241 ]], dtype=np.float32)

    if len(packed_raw.shape) >= 3:
        raw.raw_image_visible[:] = bayer2raw(packed_raw, wp, bl)
    else: # 传进来的就是raw图
        raw.raw_image_visible[:] = packed_raw
        
    out = raw.postprocess(use_camera_wb=False, user_wb=wb, half_size=False, no_auto_bright=True, 
                        output_bps=8, bright=1, user_black=None, user_sat=None)
    return out

def mask_pos_init(seq=7, max_speed=20, patch_size=512, crop_size=256):
    ps, cs = patch_size, crop_size
    pos = np.zeros((seq, 2), np.int16)
    v = np.random.randint(2*max_speed+1, size=2) - max_speed
    max_a = (max_speed // 4) * 2 // 2 + 1
    # 模拟运动
    for i in range(1, seq):
        pxmin, pymin = pos.min(axis=0)
        pxmax, pymax = pos.max(axis=0)
        # 保证不越界
        if pxmax - pxmin >= ps - cs: v[0] = -v[0]
        if pymax - pymin >= ps - cs: v[1] = -v[1]
        pos[i] = pos[i-1] + v
        a = np.random.randint(2*max_a+1, size=2) - max_a
        v = (v + a).clip(-max_speed, max_speed)
    
    pxmin, pymin = pos.min(axis=0)
    pxmax, pymax = pos.max(axis=0)
    # 保证不越界
    if pxmax - pxmin >= ps - cs:
        pos[:,0] = np.int16(pos[:,0] / (pxmax-pxmin+1))
    if pymax - pymin >= ps - cs:
        pos[:,1] = np.int16(pos[:,1] / (pymax-pymin+1))
    pxmin, pymin = pos.min(axis=0)
    pxmax, pymax = pos.max(axis=0)
    pxstart = np.random.randint(-pxmin, ps - cs - pxmax + 1)
    pystart = np.random.randint(-pymin, ps - cs - pymax + 1)
    pos[:,0] += pxstart
    pos[:,1] += pystart
    return pos

def generate_gradient_square(sx, sy, min_val_range=(0.1, 0.9), max_val_range=(0.1, 0.9), color_aug=0.5, device='cuda'):
    """
    生成一个三通道的渐变正方形图像,min_val和max_val在指定范围内随机选取,
    横轴和纵轴的渐变会根据随机选择的函数进行调整,每个通道随机缩放。

    参数:
    sx (int): 正方形的宽度。
    sy (int): 正方形的高度。
    min_val_range (tuple): 最小值的范围,默认为(0.1, 0.9)。
    max_val_range (tuple): 最大值的范围,默认为(0.1, 0.9)。
    color_aug: 随机色彩aug的概率,需要在(0, 1)之间
    device (str): 设备类型,可以是 'cuda' 或 'cpu'。

    返回:
    torch.Tensor: 生成的三通道渐变正方形图像。(sx, sy, 3)
    """
    # 随机选择最小值和最大值
    min_val = random.uniform(*min_val_range)
    max_val = random.uniform(max(min_val, max_val_range[0]), max_val_range[1])

    # 创建横轴和纵轴的线性渐变(从0到1)
    x = torch.linspace(0, 1, steps=sx, device=device, dtype=torch.float32)
    y = torch.linspace(0, 1, steps=sy, device=device, dtype=torch.float32)

    # 随机选择函数和参数
    functions = [
        lambda t: t.flip(0) if random.choice([True, False]) else t,
        lambda t: torch.sin(2 * torch.pi * random.uniform(0.25, 4) * t) / 2 + 0.5, 
        lambda t: t ** random.uniform(0.25, 4)
    ]
    x_func = random.choice(functions)
    y_func = random.choice(functions)

    # 应用随机函数
    x_grad = x_func(x)
    y_grad = y_func(y)

    # 将渐变扩展成二维矩阵
    x_matrix = x_grad.unsqueeze(0).expand(sy, sx)
    y_matrix = y_grad.unsqueeze(1).expand(sy, sx)

    # 将横轴和纵轴渐变相乘
    combined_grad = x_matrix * y_matrix

    # 扩展为三通道
    grad_3c = combined_grad.unsqueeze(0).expand(3, sy, sx)

    # 随机对每个通道进行缩放(0.5)
    color_scales = torch.tensor([random.uniform(0, 1) for _ in range(3)], device=device, dtype=torch.float32).view(3, 1, 1)
    grad_3c = grad_3c * color_scales / color_scales.max() if random.uniform(0, 1) < color_aug else grad_3c

    # 缩放到指定的最小值和最大值(默认原范围为0~1)
    scaled_grad = min_val + (max_val - min_val) * grad_3c

    return scaled_grad