├── LICENSE
├── README.md
├── dataset
├── __init__.py
├── real_benchmark_dataset.py
├── syn_sintel_dataset.py
├── syn_test_dataset.py
├── syn_vimeo_dataset.py
└── tog13_online_align_dataset.py
├── docs
├── README.md
├── _site
│ ├── README.md
│ ├── index.html
│ └── static
│ │ ├── css
│ │ ├── bulma-carousel.min.css
│ │ ├── bulma-slider.min.css
│ │ ├── bulma.css.map.txt
│ │ ├── bulma.min.css
│ │ ├── fontawesome.all.min.css
│ │ └── index.css
│ │ ├── images
│ │ └── icon.png
│ │ ├── js
│ │ ├── bulma-carousel.js
│ │ ├── bulma-carousel.min.js
│ │ ├── bulma-slider.js
│ │ ├── bulma-slider.min.js
│ │ ├── fontawesome.all.min.js
│ │ └── index.js
│ │ ├── pdfs
│ │ └── sample.pdf
│ │ └── videos
│ │ └── teaser.mp4
├── index.html
└── static
│ ├── css
│ ├── bulma-carousel.min.css
│ ├── bulma-slider.min.css
│ ├── bulma.css.map.txt
│ ├── bulma.min.css
│ ├── fontawesome.all.min.css
│ └── index.css
│ ├── images
│ ├── comp_attention.png
│ ├── comp_flow.png
│ ├── icon.png
│ ├── teaser.png
│ └── teaser_v2.png
│ ├── js
│ ├── bulma-carousel.js
│ ├── bulma-carousel.min.js
│ ├── bulma-slider.js
│ ├── bulma-slider.min.js
│ ├── fontawesome.all.min.js
│ └── index.js
│ ├── pdfs
│ └── sample.pdf
│ └── videos
│ ├── teaser.mp4
│ └── teaser_v2.mp4
├── models
├── flow_2E.py
├── flow_3E.py
├── fusion_2E.py
├── fusion_3E.py
├── loss.py
├── model_2E.py
├── model_3E.py
└── network_utils.py
├── pretrained_models
├── 2E
│ └── checkpoint.pth
└── 3E
│ └── checkpoint.pth
├── requirements.txt
├── test_2E.py
├── test_3E.py
├── test_tog13_2E.py
├── test_tog13_3E.py
├── train_2E.py
├── train_3E.py
└── utils
├── __init__.py
├── flow_viz.py
├── frame_utils.py
└── utils.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 OpenImagingLab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HDRFlow: Real-Time HDR Video Reconstruction with Large Motions
2 | ### [Project Page](https://openimaginglab.github.io/HDRFlow/) | [Paper](https://openaccess.thecvf.com/content/CVPR2024/papers/Xu_HDRFlow_Real-Time_HDR_Video_Reconstruction_with_Large_Motions_CVPR_2024_paper.pdf)
3 |
4 | Gangwei Xu, Yujin Wang, Jinwei Gu, Tianfan Xue, Xin Yang
5 | CVPR 2024
6 |
7 | 
8 | We propose a robust and efficient flow estimator tailored for real-time HDR video reconstruction, named HDRFlow. HDRFlow predicts HDR-oriented optical flow and exhibits robustness to large motions. We compare our HDR-oriented flow with RAFT's flow. RAFT's flow is sub-optimal for HDR fusion, and alignment may fail in occluded regions, leading to significant ghosting artifacts in the HDR output.
9 | Compared to previous SOTA methods, our HDRFlow enables real-time reconstruction of HDR video from video sequences captured with alternating exposures.
10 |
11 | ## Installation
12 |
13 | ### Set up the python environment
14 |
15 | ```
16 | conda create -n hdrflow python=3.10
17 | conda activate hdrflow
18 | conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cudatoolkit=11.3 -c pytorch
19 | pip install -r requirements.txt
20 | ```
21 |
22 | ### Set up datasets
23 |
24 | #### 0. Set up training datasets
25 | We utilize Vimeo-90K and Sintel datasets as our training datasets. The Vimeo-90K dataset can be downloaded at [DeepHDRVideo-Dataset](https://github.com/guanyingc/DeepHDRVideo-Dataset). The Sintel dataset can be downloaded at [BaiduYun](https://pan.baidu.com/s/1GBRyIWZmlGbTGptYX1j-zQ?pwd=gnqj). The training datasets are organized as follows:
26 | ```
27 | ├── HDRFlow/data
28 | ├── vimeo_septuplet
29 | ├── sequences
30 | ├── Sintel
31 | ├── training
32 | ├── clean
33 | ├── final
34 | ├── flow
35 | ├── reverse_flow
36 | ├── flow_2
37 | ├── reverse_flow_2
38 | ```
39 |
40 | #### 1. Set up test datasets
41 | We evaluate our method on HDR_Synthetic_Test_Dataset (Cinematic Video dataset), DeepHDRVideo, and TOG13_Dynamic_Dataset (HDRVideo dataset). These datasets can be downloaded at [DeepHDRVideo-Dataset](https://github.com/guanyingc/DeepHDRVideo-Dataset). The HDR_Synthetic_Test_Dataset contains two synthetic videos (POKER FULLSHOT and CAROUSEL FIREWORKS), DeepHDRVideo consists of both real-world dynamic scenes and static scenes that have been augmented with random global motion. The TOG13_Dynamic_Dataset does not have ground truth, so we use it for qualitative evaluation. The test datasets are organized as follows:
42 |
43 | ```
44 | ├── HDRFlow/data
45 | ├── HDR_Synthetic_Test_Dataset
46 | ├── dynamic_RGB_data_2exp_release
47 | ├── static_RGB_data_2exp_rand_motion_release
48 | ├── dynamic_RGB_data_3exp_release
49 | ├── static_RGB_data_3exp_rand_motion_release
50 | ├── TOG13_Dynamic_Dataset
51 | ```
52 |
53 | ## Evaluation and Training
54 | ### Demo
55 | You can demo a pre-trained model on ThrowingTowel-2Exp-3Stop from TOG13_Dynamic_Dataset. The TOG13_Dynamic_Dataset can be downloaded at [BaiduYun](https://pan.baidu.com/s/1GBRyIWZmlGbTGptYX1j-zQ?pwd=gnqj).
56 | ```
57 | python test_tog13_2E.py
58 | ```
59 |
60 | ### Evaluation
61 | 2 Exposures
62 | ```
63 | python test_2E.py --dataset DeepHDRVideo --dataset_dir data/dynamic_RGB_data_2exp_release
64 | python test_2E.py --dataset DeepHDRVideo --dataset_dir data/static_RGB_data_2exp_rand_motion_release
65 | python test_2E.py --dataset CinematicVideo --dataset_dir data/HDR_Synthetic_Test_Dataset
66 | python test_tog13_2E.py
67 | ```
68 |
69 | 3 Exposures
70 | ```
71 | python test_3E.py --dataset DeepHDRVideo --dataset_dir data/dynamic_RGB_data_3exp_release
72 | python test_3E.py --dataset DeepHDRVideo --dataset_dir data/static_RGB_data_3exp_rand_motion_release
73 | python test_3E.py --dataset CinematicVideo --dataset_dir data/HDR_Synthetic_Test_Dataset
74 | python test_tog13_3E.py
75 | ```
76 | ### Training
77 | 2 Exposures
78 | ```
79 | python train_2E.py
80 | ```
81 |
82 | 3 Exposures
83 | ```
84 | python train_3E.py
85 | ```
86 |
87 | ## Citation
88 |
89 | If you find this code useful for your research, please use the following BibTeX entry.
90 |
91 | ```
92 | @inproceedings{xu2024hdrflow,
93 | title={HDRFlow: Real-Time HDR Video Reconstruction with Large Motions},
94 | author={Xu, Gangwei and Wang, Yujin and Gu, Jinwei and Xue, Tianfan and Yang, Xin},
95 | booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},
96 | pages={24851--24860},
97 | year={2024}
98 | }
99 |
100 | ```
101 |
102 | ## Acknowledgement
103 | This project is based on [DeepHDRVideo](https://github.com/guanyingc/DeepHDRVideo), we thank the original authors for their excellent work.
104 |
105 |
--------------------------------------------------------------------------------
/dataset/__init__.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import os
3 | from torch.utils.data import DataLoader, ConcatDataset
4 | from .syn_vimeo_dataset import Syn_Vimeo_Dataset
5 | from .syn_sintel_dataset import Syn_Sintel_Dataset
6 |
7 | def fetch_dataloader_2E(args):
8 | train_vimeo = Syn_Vimeo_Dataset(root_dir=args.dataset_vimeo_dir, nframes=3, nexps=2, is_training=True)
9 | train_sintel_clean = Syn_Sintel_Dataset(root_dir=args.dataset_sintel_dir, dtype='clean', nframes=3, nexps=2, is_training=True)
10 | train_sintel_final = Syn_Sintel_Dataset(root_dir=args.dataset_sintel_dir, dtype='final', nframes=3, nexps=2, is_training=True)
11 | train_dataset = ConcatDataset([train_vimeo, train_sintel_clean, train_sintel_final])
12 | train_loader = DataLoader(train_dataset, batch_size=args.batch_size, shuffle=True, num_workers=args.num_workers, pin_memory=True)
13 | val_vimeo = Syn_Vimeo_Dataset(root_dir=args.dataset_vimeo_dir, nframes=3, nexps=2, is_training=False)
14 | val_loader = DataLoader(val_vimeo, batch_size=args.val_batch_size, shuffle=False, num_workers=args.num_workers, pin_memory=True)
15 | return train_loader, val_loader
16 |
17 | def fetch_dataloader_3E(args):
18 | train_vimeo = Syn_Vimeo_Dataset(root_dir=args.dataset_vimeo_dir, nframes=5, nexps=3, is_training=True)
19 | train_sintel_clean = Syn_Sintel_Dataset(root_dir=args.dataset_sintel_dir, dtype='clean', nframes=5, nexps=3, is_training=True)
20 | train_sintel_final = Syn_Sintel_Dataset(root_dir=args.dataset_sintel_dir, dtype='final', nframes=5, nexps=3, is_training=True)
21 | train_dataset = ConcatDataset([train_vimeo, train_sintel_clean, train_sintel_final])
22 | train_loader = DataLoader(train_dataset, batch_size=args.batch_size, shuffle=True, num_workers=args.num_workers, pin_memory=True)
23 | val_vimeo = Syn_Vimeo_Dataset(root_dir=args.dataset_vimeo_dir, nframes=5, nexps=3, is_training=False)
24 | val_loader = DataLoader(val_vimeo, batch_size=args.val_batch_size, shuffle=False, num_workers=args.num_workers, pin_memory=True)
25 | return train_loader, val_loader
26 |
27 |
28 |
--------------------------------------------------------------------------------
/dataset/real_benchmark_dataset.py:
--------------------------------------------------------------------------------
1 | """
2 | Dataloader for processing vimeo videos into training data
3 | """
4 | import os
5 | import numpy as np
6 | from imageio import imread
7 | import sys
8 | sys.path.append('..')
9 | import torch
10 | from torch.utils.data import Dataset
11 | from utils.utils import *
12 | np.random.seed(0)
13 |
14 | class Real_Benchmark_Dataset(Dataset):
15 | def __init__(self, root_dir, nframes, nexps):
16 |
17 | self.root_dir = root_dir
18 | self.nframes = nframes
19 | self.nexps = nexps
20 | self.scene_list = read_list(os.path.join(self.root_dir, 'scene_all.txt'))
21 |
22 | self.expos_list = []
23 | self.img_list = []
24 | self.hdrs_list = []
25 |
26 | for i in range(len(self.scene_list)):
27 | img_dir = os.path.join(self.root_dir, self.scene_list[i])
28 | img_list, hdr_list = self._load_img_hdr_list(img_dir)
29 | e_list = self._load_exposure_list(os.path.join(img_dir, 'Exposures.txt'), img_num=len(img_list))
30 |
31 | for i in range(0, len(img_list)-(self.nframes+2)+1):
32 | sub_img_list = img_list[i:i+self.nframes+2]
33 | sub_hdr_list = hdr_list[i:i+self.nframes+2]
34 | sub_e_list = e_list[i:i+self.nframes+2]
35 |
36 | self.expos_list.append(sub_e_list)
37 | self.img_list.append(sub_img_list)
38 | self.hdrs_list.append(sub_hdr_list)
39 |
40 | print('[%s] totaling %d ldrs' % (self.__class__.__name__, len(self.img_list)))
41 |
42 | def _load_img_hdr_list(self, img_dir):
43 | if os.path.exists(os.path.join(img_dir, 'img_hdr_list.txt')):
44 | img_hdr_list = np.genfromtxt(os.path.join(img_dir, 'img_hdr_list.txt'), dtype='str')
45 | img_list = img_hdr_list[:, 0]
46 | hdr_list = img_hdr_list[:, 1]
47 | else:
48 | img_list = np.genfromtxt(os.path.join(img_dir, 'img_list.txt'), dtype='str')
49 | hdr_list = ['None'] * len(img_list)
50 | img_list =[os.path.join(img_dir, img_path) for img_path in img_list]
51 | hdr_list =[os.path.join(img_dir, hdr_path) for hdr_path in hdr_list]
52 | return img_list, hdr_list
53 |
54 | def _load_exposure_list(self, expos_path, img_num):
55 | expos = np.genfromtxt(expos_path, dtype='float')
56 | expos = np.power(2, expos - expos.min()).astype(np.float32)
57 | expo_list = np.tile(expos, int(img_num / len(expos) + 1))[:img_num]
58 | return expo_list
59 |
60 | def __getitem__(self, index):
61 |
62 | ldrs = []
63 | expos = []
64 |
65 | if self.nexps == 2:
66 | img_paths, hdr_path = self.img_list[index], self.hdrs_list[index][2]
67 | exposures_all = np.array(self.expos_list[index]).astype(np.float32)
68 | for i in range(1, 4):
69 | if img_paths[i][-4:] == '.tif':
70 | img = read_16bit_tif(img_paths[i])
71 | else:
72 | img = imread(img_paths[i]) / 255.0
73 | ldrs.append(img)
74 | expos.append(exposures_all[i])
75 |
76 | elif self.nexps == 3:
77 | img_paths, hdr_path = self.img_list[index], self.hdrs_list[index][3]
78 | exposures_all = np.array(self.expos_list[index]).astype(np.float32)
79 | for i in range(1, 6):
80 | if img_paths[i][-4:] == '.tif':
81 | img = read_16bit_tif(img_paths[i])
82 | else:
83 | img = imread(img_paths[i]) / 255.0
84 | ldrs.append(img)
85 | expos.append(exposures_all[i])
86 |
87 | else:
88 | raise Exception("Unknow exposures")
89 |
90 | if os.path.exists(hdr_path):
91 | hdr = read_hdr(hdr_path)
92 | if hdr.max() > 1:
93 | hdr = hdr / hdr.max()
94 | hdr = hdr.astype(np.float32).transpose(2, 0, 1)
95 | hdr_tensor = torch.from_numpy(hdr)
96 | ldrs_tensor = []
97 | expos_tensor = []
98 | for i in range(len(ldrs)):
99 | ldr = ldrs[i].astype(np.float32).transpose(2, 0, 1)
100 | ldr_tensor = torch.from_numpy(ldr)
101 | ldrs_tensor.append(ldr_tensor)
102 | expos_tensor.append(torch.tensor(expos[i]))
103 |
104 | sample = {
105 | 'hdr_path': hdr_path.split('/')[-2]+'_'+hdr_path.split('/')[-1],
106 | 'hdr': hdr_tensor,
107 | 'ldrs': ldrs_tensor,
108 | 'expos': expos_tensor
109 | }
110 | return sample
111 |
112 | def __len__(self):
113 | return len(self.img_list)
--------------------------------------------------------------------------------
/dataset/syn_sintel_dataset.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | # from imageio import imread
4 | import sys
5 | sys.path.append('..')
6 | import torch
7 | from torch.utils.data import Dataset
8 | from utils.utils import *
9 | from utils import frame_utils
10 | from imageio import imread
11 |
12 | np.random.seed(0)
13 |
14 | class Syn_Sintel_Dataset(Dataset):
15 |
16 | def __init__(self, root_dir, dtype, nframes, nexps, is_training=True):
17 | self.root_dir = os.path.join(root_dir, dtype)
18 | self.dtype = dtype
19 | self.nframes = nframes
20 | self.nexps = nexps
21 |
22 | if is_training:
23 | list_name = 'trainlist.txt'
24 | self.repeat = 20
25 | else:
26 | list_name = 'testlist.txt'
27 | self.repeat = 1
28 |
29 | self.scene_list = read_list(os.path.join(self.root_dir, list_name))
30 | image_list = []
31 | for scene in self.scene_list:
32 | files = os.listdir(os.path.join(self.root_dir, scene))
33 | files.sort()
34 | for i in range(len(files)- (self.nframes-1)):
35 | image = files[i:i+self.nframes]
36 | if all(idx.endswith('.png') for idx in image):
37 | image_list.append([os.path.join(self.root_dir, scene, ip) for ip in image])
38 |
39 | self.image_list = image_list
40 |
41 | def __getitem__(self, index):
42 |
43 | img_paths = self.image_list[index//self.repeat]
44 | if self.nexps == 2:
45 | exposures = self._get_2exposures(index)
46 | elif self.nexps == 3:
47 | exposures = self._get_3exposures(index)
48 | else:
49 | raise Exception("Unknow exposures")
50 |
51 | """ sample parameters for the camera curves"""
52 | n, sigma = self.sample_camera_curve() # print(n, sigma)
53 |
54 | hdrs = []
55 | for img_path in img_paths:
56 | img = (imread(img_path).astype(np.float32) / 255.0).clip(0, 1)
57 | # img = read_16bit_tif(img_path)
58 | """ convert the LDR images to linear HDR image"""
59 | linear_img = self.apply_inv_sigmoid_curve(img, n, sigma)
60 | linear_img = self.discretize_to_uint16(linear_img)
61 | hdrs.append(linear_img)
62 |
63 | # p_hdr, c_hdr, n_hdr = hdrs[0], hdrs[1], hdrs[2]
64 | ### flow
65 | if self.nexps == 2:
66 | prev_flow_path = img_paths[1].replace(self.dtype, 'reverse_flow').replace('png', 'flo')
67 | nxt_flow_path = img_paths[1].replace(self.dtype, 'flow').replace('png', 'flo')
68 | prev_flow = frame_utils.read_gen(prev_flow_path)
69 | nxt_flow = frame_utils.read_gen(nxt_flow_path)
70 | flow_gts = [prev_flow, nxt_flow]
71 |
72 | elif self.nexps == 3:
73 | prev2_flow_path = img_paths[2].replace(self.dtype, 'reverse_flow_2').replace('png', 'flo')
74 | nxt1_flow_path = img_paths[2].replace(self.dtype, 'flow').replace('png', 'flo')
75 | prev1_flow_path = img_paths[2].replace(self.dtype, 'reverse_flow').replace('png', 'flo')
76 | nxt2_flow_path = img_paths[2].replace(self.dtype, 'flow_2').replace('png', 'flo')
77 |
78 | prev2_flow = frame_utils.read_gen(prev2_flow_path)
79 | nxt1_flow = frame_utils.read_gen(nxt1_flow_path)
80 | prev1_flow = frame_utils.read_gen(prev1_flow_path)
81 | nxt2_flow = frame_utils.read_gen(nxt2_flow_path)
82 |
83 | flow_gts = [prev2_flow, nxt1_flow, prev1_flow, nxt2_flow]
84 |
85 | else:
86 | raise Exception("Unknow exposures")
87 |
88 | # hdrs = prob_center_crop(hdrs)
89 | h, w, c = hdrs[0].shape
90 | # crop_h, crop_w = self.args.crop_h, self.args.crop_w
91 |
92 | crop_h, crop_w = 256, 256
93 |
94 | _hdrs = []
95 | _flow_gts = []
96 |
97 | ### random flip
98 | if np.random.rand() < 0.3: # h-flip
99 | for hdr in hdrs:
100 | _hdrs.append(hdr[:, ::-1])
101 | for flow in flow_gts:
102 | _flow_gts.append(flow[:, ::-1] * [-1.0, 1.0])
103 |
104 | hdrs = _hdrs
105 | flow_gts = _flow_gts
106 |
107 |
108 | _hdrs = []
109 | _flow_gts = []
110 |
111 | if np.random.rand() < 0.1: # v-flip
112 |
113 | for hdr in hdrs:
114 | _hdrs.append(hdr[::-1, :])
115 | for flow in flow_gts:
116 | _flow_gts.append(flow[::-1, :] * [1.0, -1.0])
117 |
118 | hdrs = _hdrs
119 | flow_gts = _flow_gts
120 |
121 | ### random crop
122 | y0 = np.random.randint(0, hdrs[0].shape[0] - crop_h)
123 | x0 = np.random.randint(0, hdrs[0].shape[1] - crop_w)
124 |
125 | _hdrs = []
126 | _flow_gts = []
127 |
128 | for hdr in hdrs:
129 | _hdrs.append(hdr[y0:y0+crop_h, x0:x0+crop_w])
130 | for flow in flow_gts:
131 | _flow_gts.append(flow[y0:y0+crop_h, x0:x0+crop_w])
132 |
133 | hdrs = _hdrs
134 | flow_gts = _flow_gts
135 |
136 | color_permute = np.random.permutation(3)
137 | for i in range(len(hdrs)):
138 | hdrs[i] = hdrs[i][:,:,color_permute]
139 |
140 | hdrs, ldrs = self.re_expose_ldrs(hdrs, exposures)
141 | ldrs_tensor = []
142 | hdrs_tensor = []
143 | expos_tensor = []
144 | for i in range(len(ldrs)):
145 | ldr = ldrs[i].astype(np.float32).transpose(2, 0, 1)
146 | ldr_tensor = torch.from_numpy(ldr)
147 | ldrs_tensor.append(ldr_tensor)
148 |
149 | hdr = hdrs[i].astype(np.float32).transpose(2, 0, 1)
150 | hdr_tensor = torch.from_numpy(hdr)
151 | hdrs_tensor.append(hdr_tensor)
152 |
153 | expos_tensor.append(torch.tensor(exposures[i]))
154 |
155 | flow_gts_tensor = []
156 | for i in range(len(flow_gts)):
157 | flow = flow_gts[i].astype(np.float32).transpose(2, 0, 1)
158 | flow_tensor = torch.from_numpy(flow)
159 | flow_gts_tensor.append(flow_tensor)
160 |
161 | flow_mask = torch.tensor(1.)
162 |
163 | sample = {
164 | 'hdrs': hdrs_tensor,
165 | 'ldrs': ldrs_tensor,
166 | 'expos': expos_tensor,
167 | 'flow_gts': flow_gts_tensor,
168 | 'flow_mask': flow_mask
169 | }
170 | return sample
171 |
172 | def sample_camera_curve(self):
173 | n = np.clip(np.random.normal(0.65, 0.1), 0.4, 0.9)
174 | sigma = np.clip(np.random.normal(0.6, 0.1), 0.4, 0.8)
175 | return n, sigma
176 |
177 | def apply_sigmoid_curve(self, x, n, sigma):
178 | y = (1 + sigma) * np.power(x, n) / (np.power(x, n) + sigma)
179 | return y
180 |
181 | def apply_inv_sigmoid_curve(self, y, n, sigma):
182 | x = np.power((sigma * y) / (1 + sigma - y), 1/n)
183 | return x
184 |
185 | def apply_inv_s_curve(self, y):
186 | x = 0.5 - np.sin(np.arcsin(1 - 2*y)/3.0)
187 | return x
188 |
189 | def discretize_to_uint16(self, img):
190 | max_int = 2**16-1
191 | img_uint16 = np.uint16(img * max_int).astype(np.float32) / max_int
192 | return img_uint16
193 |
194 | def _get_2exposures(self, index):
195 | cur_high = True if np.random.uniform() > 0.5 else False
196 | exposures = np.ones(self.nframes, dtype=np.float32)
197 | high_expo = np.random.choice([4., 8.])
198 |
199 | if cur_high:
200 | for i in range(0, self.nframes, 2):
201 | exposures[i] = high_expo
202 | else:
203 | for i in range(1, self.nframes, 2):
204 | exposures[i] = high_expo
205 | return exposures
206 |
207 | def _get_3exposures(self, index):
208 | if index % self.nexps == 0:
209 | exp1 = 1
210 | elif index % self.nexps == 1:
211 | exp1 = 4
212 | else:
213 | exp1 = 16
214 | expos = [exp1]
215 | for i in range(1, self.nframes):
216 | if expos[-1] == 1:
217 | expos.append(4)
218 | elif expos[-1] == 4:
219 | expos.append(16)
220 | elif expos[-1] == 16:
221 | expos.append(1)
222 | else:
223 | raise Exception('Unknown expos %d' % expos[-1])
224 | exposures = np.array(expos).astype(np.float32)
225 | return exposures
226 |
227 | def re_expose_ldrs(self, hdrs, exposures):
228 | mid = len(hdrs) // 2
229 |
230 | new_hdrs = []
231 | if self.nexps == 3:
232 | if exposures[mid] == 1:
233 | factor = np.random.uniform(0.1, 0.8)
234 | anchor = hdrs[mid].max()
235 | new_anchor = anchor * factor
236 | else: # exposures[mid] == 4 or 8
237 | percent = np.random.uniform(98, 100)
238 | anchor = np.percentile(hdrs[mid], percent)
239 | new_anchor = np.random.uniform(anchor, 1)
240 | else:
241 | if exposures[mid] == 1: # low exposure reference
242 | factor = np.random.uniform(0.1, 0.8)
243 | anchor = hdrs[mid].max()
244 | new_anchor = anchor * factor
245 | else: # high exposure reference
246 | percent = np.random.uniform(98, 100)
247 | anchor = np.percentile(hdrs[mid], percent)
248 | new_anchor = np.random.uniform(anchor, 1)
249 |
250 | for idx, hdr in enumerate(hdrs):
251 | new_hdr = (hdr / (anchor + 1e-8) * new_anchor).clip(0, 1)
252 | new_hdrs.append(new_hdr)
253 |
254 | ldrs = []
255 | for i in range(len(new_hdrs)):
256 | ldr = hdr_to_ldr(new_hdrs[i], exposures[i])
257 | ldrs.append(ldr)
258 | return new_hdrs, ldrs
259 |
260 | def __len__(self):
261 | return len(self.image_list) * self.repeat
--------------------------------------------------------------------------------
/dataset/syn_test_dataset.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | from imageio import imread
4 | import sys
5 | sys.path.append('..')
6 | import torch
7 | from torch.utils.data import Dataset
8 | from utils.utils import *
9 | np.random.seed(0)
10 |
11 | class Syn_Test_Dataset(Dataset):
12 | def __init__(self, root_dir, nframes, nexps):
13 |
14 | self.root_dir = root_dir
15 | self.nframes = nframes
16 | self.nexps = nexps
17 | self.scene_list = read_list(os.path.join(self.root_dir, 'scenes_2expo.txt'))
18 |
19 | self.expos_list = []
20 | self.img_list = []
21 | self.hdrs_list = []
22 |
23 | for i in range(len(self.scene_list)):
24 | img_dir = os.path.join(self.root_dir, 'Images', self.scene_list[i])
25 | img_list, hdr_list = self._load_img_hdr_list(img_dir)
26 | e_list = self._load_exposure_list(os.path.join(img_dir, 'Exposures.txt'), img_num=len(img_list))
27 | img_list, hdr_list, e_list = self._lists_to_paired_lists([img_list, hdr_list, e_list])
28 |
29 | self.expos_list += e_list
30 | self.img_list += img_list
31 | self.hdrs_list += hdr_list
32 |
33 | print('[%s] totaling %d ldrs' % (self.__class__.__name__, len(self.img_list)))
34 |
35 | def _load_img_hdr_list(self, img_dir):
36 | scene_list = np.genfromtxt(os.path.join(img_dir, 'img_list.txt'), dtype='str')
37 | img_list = ['%s.tif' % name for name in scene_list]
38 | hdr_list = ['%s.hdr' % name for name in scene_list]
39 | img_list =[os.path.join(img_dir, img_path) for img_path in img_list]
40 | hdr_list =[os.path.join(img_dir, hdr_path) for hdr_path in hdr_list]
41 | return img_list, hdr_list
42 |
43 | def _load_exposure_list(self, expos_path, img_num):
44 | expos = np.genfromtxt(expos_path, dtype='float')
45 | expos = np.power(2, expos - expos.min()).astype(np.float32)
46 | expo_list = np.tile(expos, int(img_num / len(expos) + 1))[:img_num]
47 | return expo_list
48 |
49 | def _lists_to_paired_lists(self, lists):
50 | paired_lists = []
51 |
52 | for l in lists:
53 | if (self.nexps == 2 and self.nframes == 3) or (self.nexps == 3 and self.nframes == 5):
54 | l = l[1:-1]
55 | paired_list = []
56 | paired_list.append(l[: len(l) - self.nframes + 1])
57 | for j in range(1, self.nframes):
58 | start_idx, end_idx = j, len(l) - self.nframes + 1 + j
59 | paired_list.append(l[start_idx: end_idx])
60 | paired_lists.append(np.stack(paired_list, 1).tolist()) # Nxframes
61 | return paired_lists
62 |
63 | def __getitem__(self, index):
64 |
65 | ldrs = []
66 | expos = []
67 | if self.nexps == 2:
68 | img_paths, hdr_path = self.img_list[index], self.hdrs_list[index][1]
69 | exposures_all = np.array(self.expos_list[index]).astype(np.float32)
70 | for i in range(0, 3):
71 | img = read_16bit_tif(img_paths[i])
72 | ldrs.append(img)
73 | expos.append(exposures_all[i])
74 |
75 | elif self.nexps == 3:
76 | img_paths, hdr_path = self.img_list[index], self.hdrs_list[index][2]
77 | exposures_all = np.array(self.expos_list[index]).astype(np.float32)
78 | for i in range(0, 5):
79 | img = read_16bit_tif(img_paths[i])
80 | ldrs.append(img)
81 | expos.append(exposures_all[i])
82 |
83 | else:
84 | raise Exception("Unknow exposures")
85 |
86 | if os.path.exists(hdr_path):
87 | hdr = read_hdr(hdr_path)
88 | hdr = hdr.astype(np.float32).transpose(2, 0, 1)
89 | hdr_tensor = torch.from_numpy(hdr)
90 | ldrs_tensor = []
91 | expos_tensor = []
92 | for i in range(len(ldrs)):
93 | ldr = ldrs[i].astype(np.float32).transpose(2, 0, 1)
94 | ldr_tensor = torch.from_numpy(ldr)
95 | ldrs_tensor.append(ldr_tensor)
96 | expos_tensor.append(torch.tensor(expos[i]))
97 |
98 | sample = {
99 | 'hdr_path': hdr_path.split('/')[-1],
100 | 'hdr': hdr_tensor,
101 | 'ldrs': ldrs_tensor,
102 | 'expos': expos_tensor
103 | }
104 | return sample
105 |
106 | def __len__(self):
107 | return len(self.img_list)
108 |
109 |
--------------------------------------------------------------------------------
/dataset/syn_vimeo_dataset.py:
--------------------------------------------------------------------------------
1 | """
2 | Dataloader for processing vimeo videos into training data
3 | """
4 | import os
5 | import numpy as np
6 | from imageio import imread
7 |
8 | import sys
9 | sys.path.append('..')
10 | import torch
11 | from torch.utils.data import Dataset
12 | from utils.utils import *
13 |
14 | np.random.seed(0)
15 |
16 | class Syn_Vimeo_Dataset(Dataset):
17 |
18 | def __init__(self, root_dir, nframes, nexps, is_training=True):
19 |
20 | self.root_dir = root_dir
21 | self.nframes = nframes
22 | self.nexps = nexps
23 |
24 | if is_training:
25 | list_name = 'sep_trainlist.txt'
26 | self.repeat = 1
27 | else:
28 | list_name = 'sep_testlist.txt'
29 | self.repeat = 1
30 |
31 | self.patch_list = read_list(os.path.join(self.root_dir, list_name))
32 |
33 | if not is_training:
34 | self.patch_list = self.patch_list[:100] # only use 100 val patches
35 |
36 | def __getitem__(self, index):
37 |
38 | img_dir = os.path.join(self.root_dir, 'sequences', self.patch_list[index // self.repeat])
39 | img_idxs = sorted(np.random.permutation(7)[:self.nframes] + 1)
40 | if np.random.random() > 0.5: # inverse time order
41 | img_idxs = img_idxs[::-1]
42 | img_paths = [os.path.join(img_dir, 'im%d.png' % idx) for idx in img_idxs]
43 |
44 | if self.nexps == 2:
45 | exposures = self._get_2exposures(index)
46 | elif self.nexps == 3:
47 | exposures = self._get_3exposures(index)
48 | else:
49 | raise Exception("Unknow exposures")
50 |
51 | """ sample parameters for the camera curves"""
52 | n, sigma = self.sample_camera_curve() # print(n, sigma)
53 |
54 | hdrs = []
55 | for img_path in img_paths:
56 | img = (imread(img_path).astype(np.float32) / 255.0).clip(0, 1)
57 |
58 | """ convert the LDR images to linear HDR image"""
59 | linear_img = self.apply_inv_sigmoid_curve(img, n, sigma)
60 | linear_img = self.discretize_to_uint16(linear_img)
61 | hdrs.append(linear_img)
62 |
63 | h, w, c = hdrs[0].shape
64 | crop_h, crop_w = 256, 256
65 |
66 | hdrs = random_flip_lrud(hdrs)
67 | hdrs = random_crop(hdrs, [crop_h, crop_w])
68 | color_permute = np.random.permutation(3)
69 | for i in range(len(hdrs)):
70 | hdrs[i] = hdrs[i][:,:,color_permute]
71 |
72 | hdrs, ldrs = self.re_expose_ldrs(hdrs, exposures)
73 | ldrs_tensor = []
74 | hdrs_tensor = []
75 | expos_tensor = []
76 | for i in range(len(ldrs)):
77 | ldr = ldrs[i].astype(np.float32).transpose(2, 0, 1)
78 | ldr_tensor = torch.from_numpy(ldr)
79 | ldrs_tensor.append(ldr_tensor)
80 |
81 | hdr = hdrs[i].astype(np.float32).transpose(2, 0, 1)
82 | hdr_tensor = torch.from_numpy(hdr)
83 | hdrs_tensor.append(hdr_tensor)
84 |
85 | expos_tensor.append(torch.tensor(exposures[i]))
86 |
87 | flow_gts = []
88 | if self.nexps == 2:
89 |
90 | prev_flow = torch.zeros(2, crop_h, crop_w)
91 | nxt_flow = torch.zeros(2, crop_h, crop_w)
92 | flow_gts.append(prev_flow)
93 | flow_gts.append(nxt_flow)
94 |
95 | elif self.nexps == 3:
96 | prev2_flow = torch.zeros(2, crop_h, crop_w)
97 | nxt1_flow = torch.zeros(2, crop_h, crop_w)
98 | prev1_flow = torch.zeros(2, crop_h, crop_w)
99 | nxt2_flow = torch.zeros(2, crop_h, crop_w)
100 |
101 | flow_gts.append(prev2_flow)
102 | flow_gts.append(nxt1_flow)
103 | flow_gts.append(prev1_flow)
104 | flow_gts.append(nxt2_flow)
105 |
106 | else:
107 | raise Exception("Unknow exposures")
108 |
109 | flow_mask = torch.tensor(0.)
110 |
111 | sample = {
112 | 'hdrs': hdrs_tensor,
113 | 'ldrs': ldrs_tensor,
114 | 'expos': expos_tensor,
115 | 'flow_gts': flow_gts,
116 | 'flow_mask': flow_mask
117 | }
118 | return sample
119 |
120 | def sample_camera_curve(self):
121 | n = np.clip(np.random.normal(0.65, 0.1), 0.4, 0.9)
122 | sigma = np.clip(np.random.normal(0.6, 0.1), 0.4, 0.8)
123 | return n, sigma
124 |
125 | def apply_sigmoid_curve(self, x, n, sigma):
126 | y = (1 + sigma) * np.power(x, n) / (np.power(x, n) + sigma)
127 | return y
128 |
129 | def apply_inv_sigmoid_curve(self, y, n, sigma):
130 | x = np.power((sigma * y) / (1 + sigma - y), 1/n)
131 | return x
132 |
133 | def apply_inv_s_curve(self, y):
134 | x = 0.5 - np.sin(np.arcsin(1 - 2*y)/3.0)
135 | return x
136 |
137 | def discretize_to_uint16(self, img):
138 | max_int = 2**16-1
139 | img_uint16 = np.uint16(img * max_int).astype(np.float32) / max_int
140 | return img_uint16
141 |
142 | def _get_2exposures(self, index):
143 | cur_high = True if np.random.uniform() > 0.5 else False
144 | exposures = np.ones(self.nframes, dtype=np.float32)
145 | high_expo = np.random.choice([4., 8.])
146 |
147 | if cur_high:
148 | for i in range(0, self.nframes, 2):
149 | exposures[i] = high_expo
150 | else:
151 | for i in range(1, self.nframes, 2):
152 | exposures[i] = high_expo
153 | return exposures
154 |
155 | def _get_3exposures(self, index):
156 | if index % self.nexps == 0:
157 | exp1 = 1
158 | elif index % self.nexps == 1:
159 | exp1 = 4
160 | else:
161 | exp1 = 16
162 | expos = [exp1]
163 | for i in range(1, self.nframes):
164 | if expos[-1] == 1:
165 | expos.append(4)
166 | elif expos[-1] == 4:
167 | expos.append(16)
168 | elif expos[-1] == 16:
169 | expos.append(1)
170 | else:
171 | raise Exception('Unknown expos %d' % expos[-1])
172 | exposures = np.array(expos).astype(np.float32)
173 | return exposures
174 |
175 | def re_expose_ldrs(self, hdrs, exposures):
176 | mid = len(hdrs) // 2
177 | new_hdrs = []
178 | if self.nexps == 3:
179 | if exposures[mid] == 1:
180 | factor = np.random.uniform(0.1, 0.8)
181 | anchor = hdrs[mid].max()
182 | new_anchor = anchor * factor
183 | else: # exposures[mid] == 4 or 8
184 | percent = np.random.uniform(98, 100)
185 | anchor = np.percentile(hdrs[mid], percent)
186 | new_anchor = np.random.uniform(anchor, 1)
187 | else:
188 | if exposures[mid] == 1: # low exposure reference
189 | factor = np.random.uniform(0.1, 0.8)
190 | anchor = hdrs[mid].max()
191 | new_anchor = anchor * factor
192 | else: # high exposure reference
193 | percent = np.random.uniform(98, 100)
194 | anchor = np.percentile(hdrs[mid], percent)
195 | new_anchor = np.random.uniform(anchor, 1)
196 | for idx, hdr in enumerate(hdrs):
197 | new_hdr = (hdr / (anchor + 1e-8) * new_anchor).clip(0, 1)
198 | new_hdrs.append(new_hdr)
199 |
200 | ldrs = []
201 | for i in range(len(new_hdrs)):
202 | ldr = hdr_to_ldr(new_hdrs[i], exposures[i])
203 | ldrs.append(ldr)
204 | return new_hdrs, ldrs
205 |
206 | def __len__(self):
207 | return len(self.patch_list) * self.repeat
--------------------------------------------------------------------------------
/dataset/tog13_online_align_dataset.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | from imageio import imread
4 | import sys
5 | sys.path.append('..')
6 | import torch
7 | from torch.utils.data import Dataset
8 | import scipy.io as sio
9 | from utils.utils import *
10 | np.random.seed(0)
11 |
12 | class TOG13_online_align_Dataset(Dataset):
13 | def __init__(self, root_dir, nframes, nexps, align=True):
14 |
15 | self.root_dir = root_dir
16 | self.nframes = nframes
17 | self.nexps = nexps
18 | crf_path = root_dir.split('/')
19 | crf_path = crf_path[:-1]
20 | crf_path = '/'.join(crf_path)
21 | self.crf = sio.loadmat(os.path.join(crf_path, 'BaslerCRF.mat'))['BaslerCRF']
22 | self.align = align
23 | img_list, hdr_list = self._load_img_hdr_list(self.root_dir)
24 | e_list = self._load_exposure_list(os.path.join(self.root_dir, 'Exposures.txt'), img_num=len(img_list))
25 | self.imgs_list, self.hdrs_list, self.expos_list = self._lists_to_paired_lists([img_list, hdr_list, e_list])
26 | print('[%s] totaling %d ldrs' % (self.__class__.__name__, len(self.imgs_list)))
27 |
28 | def _load_img_hdr_list(self, img_dir):
29 | if os.path.exists(os.path.join(img_dir, 'img_hdr_list.txt')):
30 | img_hdr_list = np.genfromtxt(os.path.join(img_dir, 'img_hdr_list.txt'), dtype='str')
31 | img_list = img_hdr_list[:, 0]
32 | hdr_list = img_hdr_list[:, 1]
33 | else:
34 | img_list = np.genfromtxt(os.path.join(img_dir, 'img_list.txt'), dtype='str')
35 | hdr_list = ['None'] * len(img_list)
36 | img_list =[os.path.join(img_dir, img_path) for img_path in img_list]
37 | hdr_list =[os.path.join(img_dir, hdr_path) for hdr_path in hdr_list]
38 | return img_list, hdr_list
39 |
40 | def _load_exposure_list(self, expos_path, img_num):
41 | expos = np.genfromtxt(expos_path, dtype='float')
42 | expos = np.power(2, expos - expos.min()).astype(np.float32)
43 | expo_list = np.tile(expos, int(img_num / len(expos) + 1))[:img_num]
44 | return expo_list
45 |
46 | def _lists_to_paired_lists(self, lists):
47 | paired_lists = []
48 |
49 | for l in lists:
50 | if (self.nexps == 2 and self.nframes == 3) or (self.nexps == 3 and self.nframes == 5):
51 | l = l[1:-1]
52 | paired_list = []
53 | paired_list.append(l[: len(l) - self.nframes + 1])
54 | for j in range(1, self.nframes):
55 | start_idx, end_idx = j, len(l) - self.nframes + 1 + j
56 | paired_list.append(l[start_idx: end_idx])
57 | paired_lists.append(np.stack(paired_list, 1).tolist()) # Nxframes
58 | return paired_lists
59 |
60 | def load_affine_matrices(self, img_path, h, w):
61 | dir_name, img_name = os.path.dirname(img_path), os.path.basename(img_path)
62 | cv2_match = np.genfromtxt(os.path.join(dir_name, 'Affine_Trans_Matrices', img_name[:-4]+'_match.txt'), dtype=np.float32)
63 | # For two exposure: cv2_match [2, 6], row 1->2: cur->prev, cur->next
64 | # For three exposure: cv2_match [4, 6], row1->4: cur->prev2, cur->prev, cur->next, cur->next2
65 |
66 | n_matches =cv2_match.shape[0]
67 | if self.nexps == 2:
68 | assert (n_matches == 2)
69 | elif self.nexps == 3:
70 | assert (n_matches == 4)
71 |
72 | cv2_match = cv2_match.reshape(n_matches, 2, 3)
73 | theta = np.zeros((n_matches, 2, 3)).astype(np.float32) # Theta for affine transformation in pytorch
74 | for mi in range(n_matches):
75 | theta[mi] = cvt_MToTheta(cv2_match[mi], w, h)
76 | return theta
77 |
78 | def __getitem__(self, index):
79 | ldrs = []
80 | expos = []
81 | matches = []
82 | img_paths = self.imgs_list[index]
83 | exposures_all = np.array(self.expos_list[index]).astype(np.float32)
84 | for i in range(0, self.nframes):
85 | img = apply_gamma(read_16bit_tif(img_paths[i], crf=self.crf), gamma=2.2)
86 | ldrs.append(img)
87 | expos.append(exposures_all[i])
88 | if self.align:
89 | match = self.load_affine_matrices(img_paths[i], img.shape[0], img.shape[1])
90 | matches.append(match)
91 | ldrs_tensor = []
92 | expos_tensor = []
93 | matches_tensor = []
94 | for i in range(len(ldrs)):
95 | ldr = ldrs[i].astype(np.float32).transpose(2, 0, 1)
96 | ldr_tensor = torch.from_numpy(ldr)
97 | ldrs_tensor.append(ldr_tensor)
98 | expos_tensor.append(torch.tensor(expos[i]))
99 | matches_tensor.append(torch.tensor(matches[i]))
100 |
101 | sample = {
102 | 'ldrs': ldrs_tensor,
103 | 'expos': expos_tensor,
104 | 'matches': matches_tensor
105 | }
106 | return sample
107 |
108 | def __len__(self):
109 | return len(self.imgs_list)
110 |
111 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Academic Project Page Template
2 | This is an academic paper project page template.
3 |
4 |
5 | Example project pages built using this template are:
6 | - https://www.vision.huji.ac.il/deepsim/
7 | - https://www.vision.huji.ac.il/3d_ads/
8 | - https://www.vision.huji.ac.il/ssrl_ad/
9 | - https://www.vision.huji.ac.il/conffusion/
10 |
11 |
12 | ## Start using the template
13 | To start using the template click on `Use this Template`.
14 |
15 | The template uses html for controlling the content and css for controlling the style.
16 | To edit the websites contents edit the `index.html` file. It contains different HTML "building blocks", use whichever ones you need and comment out the rest.
17 |
18 | **IMPORTANT!** Make sure to replace the `favicon.ico` under `static/images/` with one of your own, otherwise your favicon is going to be a dreambooth image of me.
19 |
20 | ## Components
21 | - Teaser video
22 | - Images Carousel
23 | - Youtube embedding
24 | - Video Carousel
25 | - PDF Poster
26 | - Bibtex citation
27 |
28 | ## Tips:
29 | - The `index.html` file contains comments instructing you what to replace, you should follow these comments.
30 | - The `meta` tags in the `index.html` file are used to provide metadata about your paper
31 | (e.g. helping search engine index the website, showing a preview image when sharing the website, etc.)
32 | - The resolution of images and videos can usually be around 1920-2048, there rarely a need for better resolution that take longer to load.
33 | - All the images and videos you use should be compressed to allow for fast loading of the website (and thus better indexing by search engines). For images, you can use [TinyPNG](https://tinypng.com), for videos you can need to find the tradeoff between size and quality.
34 | - When using large video files (larger than 10MB), it's better to use youtube for hosting the video as serving the video from the website can take time.
35 | - Using a tracker can help you analyze the traffic and see where users came from. [statcounter](https://statcounter.com) is a free, easy to use tracker that takes under 5 minutes to set up.
36 | - This project page can also be made into a github pages website.
37 | - Replace the favicon to one of your choosing (the default one is of the Hebrew University).
38 | - Suggestions, improvements and comments are welcome, simply open an issue or contact me. You can find my contact information at [https://pages.cs.huji.ac.il/eliahu-horwitz/](https://pages.cs.huji.ac.il/eliahu-horwitz/)
39 |
40 | ## Acknowledgments
41 | Parts of this project page were adopted from the [Nerfies](https://nerfies.github.io/) page.
42 |
43 | ## Website License
44 | This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License .
45 |
--------------------------------------------------------------------------------
/docs/_site/README.md:
--------------------------------------------------------------------------------
1 | # Academic Project Page Template
2 | This is an academic paper project page template.
3 |
4 |
5 | Example project pages built using this template are:
6 | - https://www.vision.huji.ac.il/deepsim/
7 | - https://www.vision.huji.ac.il/3d_ads/
8 | - https://www.vision.huji.ac.il/ssrl_ad/
9 | - https://www.vision.huji.ac.il/conffusion/
10 |
11 |
12 | ## Start using the template
13 | To start using the template click on `Use this Template`.
14 |
15 | The template uses html for controlling the content and css for controlling the style.
16 | To edit the websites contents edit the `index.html` file. It contains different HTML "building blocks", use whichever ones you need and comment out the rest.
17 |
18 | **IMPORTANT!** Make sure to replace the `favicon.ico` under `static/images/` with one of your own, otherwise your favicon is going to be a dreambooth image of me.
19 |
20 | ## Components
21 | - Teaser video
22 | - Images Carousel
23 | - Youtube embedding
24 | - Video Carousel
25 | - PDF Poster
26 | - Bibtex citation
27 |
28 | ## Tips:
29 | - The `index.html` file contains comments instructing you what to replace, you should follow these comments.
30 | - The `meta` tags in the `index.html` file are used to provide metadata about your paper
31 | (e.g. helping search engine index the website, showing a preview image when sharing the website, etc.)
32 | - The resolution of images and videos can usually be around 1920-2048, there rarely a need for better resolution that take longer to load.
33 | - All the images and videos you use should be compressed to allow for fast loading of the website (and thus better indexing by search engines). For images, you can use [TinyPNG](https://tinypng.com), for videos you can need to find the tradeoff between size and quality.
34 | - When using large video files (larger than 10MB), it's better to use youtube for hosting the video as serving the video from the website can take time.
35 | - Using a tracker can help you analyze the traffic and see where users came from. [statcounter](https://statcounter.com) is a free, easy to use tracker that takes under 5 minutes to set up.
36 | - This project page can also be made into a github pages website.
37 | - Replace the favicon to one of your choosing (the default one is of the Hebrew University).
38 | - Suggestions, improvements and comments are welcome, simply open an issue or contact me. You can find my contact information at [https://pages.cs.huji.ac.il/eliahu-horwitz/](https://pages.cs.huji.ac.il/eliahu-horwitz/)
39 |
40 | ## Acknowledgments
41 | Parts of this project page were adopted from the [Nerfies](https://nerfies.github.io/) page.
42 |
43 | ## Website License
44 | This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License .
45 |
--------------------------------------------------------------------------------
/docs/_site/index.html:
--------------------------------------------------------------------------------
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 | Event-Based Motion Magnification
28 |
29 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
Event-Based Motion Magnification
56 |
72 |
73 |
74 | 1 Shanghai AI Laboratory, 2 Zhejiang University,
75 | 3 The Chinese University of Hong Kong
76 | † Indicates Corresponding Author
77 |
78 |
79 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
138 |
139 |
140 | Out system enables a broad and cost-effective amplification of high-frequency motions.
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
Abstract
152 |
153 |
154 | Detecting and magnifying imperceptible high-frequency motions in real-world scenarios has substantial implications for industrial and medical applications. These motions are characterized by small amplitudes and high frequencies. Traditional motion magnification methods rely on costly high-speed cameras or active light sources, which limit the scope of their applications. In this work, we propose a dual-camera system consisting of an event camera and a conventional RGB camera for video motion magnification, containing temporally-dense information from the event stream and spatially-dense data from the RGB images. This innovative combination enables a broad and cost-effective amplification of high-frequency motions. By revisiting the physical camera model, we observe that estimating motion direction and magnitude necessitates the integration of event streams with additional image features. On this basis, we propose a novel deep network for event-based video motion magnification that addresses two primary challenges: firstly, the high frequency of motion induces a large number of interpolated frames (up to 80), which our network mitigates with a Second-order Recurrent Propagation module for better handling of long-term frame interpolations; and secondly, magnifying subtle motions is sensitive to noise, which we address by utilizing a temporal filter to amplify motion at specific frequencies and reduce noise impact. We demonstrate the effectiveness and accuracy of our dual-camera system and network through extensive experiments in magnifying small-amplitude, high-frequency motions, offering a cost-effective and flexible solution for motion detection and magnification.
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
Video
167 |
168 |
169 |
170 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
188 |
189 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
BibTeX
206 |
BibTex Code Here
207 |
208 |
209 |
210 |
211 |
212 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
--------------------------------------------------------------------------------
/docs/_site/static/css/bulma-carousel.min.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.slider{position:relative;width:100%}.slider-container{display:flex;flex-wrap:nowrap;flex-direction:row;overflow:hidden;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);min-height:100%}.slider-container.is-vertical{flex-direction:column}.slider-container .slider-item{flex:none}.slider-container .slider-item .image.is-covered img{-o-object-fit:cover;object-fit:cover;-o-object-position:center center;object-position:center center;height:100%;width:100%}.slider-container .slider-item .video-container{height:0;padding-bottom:0;padding-top:56.25%;margin:0;position:relative}.slider-container .slider-item .video-container.is-1by1,.slider-container .slider-item .video-container.is-square{padding-top:100%}.slider-container .slider-item .video-container.is-4by3{padding-top:75%}.slider-container .slider-item .video-container.is-21by9{padding-top:42.857143%}.slider-container .slider-item .video-container embed,.slider-container .slider-item .video-container iframe,.slider-container .slider-item .video-container object{position:absolute;top:0;left:0;width:100%!important;height:100%!important}.slider-navigation-next,.slider-navigation-previous{display:flex;justify-content:center;align-items:center;position:absolute;width:42px;height:42px;background:#fff center center no-repeat;background-size:20px 20px;border:1px solid #fff;border-radius:25091983px;box-shadow:0 2px 5px #3232321a;top:50%;margin-top:-20px;left:0;cursor:pointer;transition:opacity .3s,-webkit-transform .3s;transition:transform .3s,opacity .3s;transition:transform .3s,opacity .3s,-webkit-transform .3s}.slider-navigation-next:hover,.slider-navigation-previous:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.slider-navigation-next.is-hidden,.slider-navigation-previous.is-hidden{display:none;opacity:0}.slider-navigation-next svg,.slider-navigation-previous svg{width:25%}.slider-navigation-next{left:auto;right:0;background:#fff center center no-repeat;background-size:20px 20px}.slider-pagination{display:none;justify-content:center;align-items:center;position:absolute;bottom:0;left:0;right:0;padding:.5rem 1rem;text-align:center}.slider-pagination .slider-page{background:#fff;width:10px;height:10px;border-radius:25091983px;display:inline-block;margin:0 3px;box-shadow:0 2px 5px #3232321a;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;cursor:pointer}.slider-pagination .slider-page.is-active,.slider-pagination .slider-page:hover{-webkit-transform:scale(1.4);transform:scale(1.4)}@media screen and (min-width:800px){.slider-pagination{display:flex}}.hero.has-carousel{position:relative}.hero.has-carousel+.hero-body,.hero.has-carousel+.hero-footer,.hero.has-carousel+.hero-head{z-index:10;overflow:hidden}.hero.has-carousel .hero-carousel{position:absolute;top:0;left:0;bottom:0;right:0;height:auto;border:none;margin:auto;padding:0;z-index:0}.hero.has-carousel .hero-carousel .slider{width:100%;max-width:100%;overflow:hidden;height:100%!important;max-height:100%;z-index:0}.hero.has-carousel .hero-carousel .slider .has-background{max-height:100%}.hero.has-carousel .hero-carousel .slider .has-background .is-background{-o-object-fit:cover;object-fit:cover;-o-object-position:center center;object-position:center center;height:100%;width:100%}.hero.has-carousel .hero-body{margin:0 3rem;z-index:10}
--------------------------------------------------------------------------------
/docs/_site/static/css/bulma-slider.min.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}input[type=range].slider{-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:1rem 0;background:0 0;touch-action:none}input[type=range].slider.is-fullwidth{display:block;width:100%}input[type=range].slider:focus{outline:0}input[type=range].slider:not([orient=vertical])::-webkit-slider-runnable-track{width:100%}input[type=range].slider:not([orient=vertical])::-moz-range-track{width:100%}input[type=range].slider:not([orient=vertical])::-ms-track{width:100%}input[type=range].slider:not([orient=vertical]).has-output+output,input[type=range].slider:not([orient=vertical]).has-output-tooltip+output{width:3rem;background:#4a4a4a;border-radius:4px;padding:.4rem .8rem;font-size:.75rem;line-height:.75rem;text-align:center;text-overflow:ellipsis;white-space:nowrap;color:#fff;overflow:hidden;pointer-events:none;z-index:200}input[type=range].slider:not([orient=vertical]).has-output-tooltip:disabled+output,input[type=range].slider:not([orient=vertical]).has-output:disabled+output{opacity:.5}input[type=range].slider:not([orient=vertical]).has-output{display:inline-block;vertical-align:middle;width:calc(100% - (4.2rem))}input[type=range].slider:not([orient=vertical]).has-output+output{display:inline-block;margin-left:.75rem;vertical-align:middle}input[type=range].slider:not([orient=vertical]).has-output-tooltip{display:block}input[type=range].slider:not([orient=vertical]).has-output-tooltip+output{position:absolute;left:0;top:-.1rem}input[type=range].slider[orient=vertical]{-webkit-appearance:slider-vertical;-moz-appearance:slider-vertical;appearance:slider-vertical;-webkit-writing-mode:bt-lr;-ms-writing-mode:bt-lr;writing-mode:bt-lr}input[type=range].slider[orient=vertical]::-webkit-slider-runnable-track{height:100%}input[type=range].slider[orient=vertical]::-moz-range-track{height:100%}input[type=range].slider[orient=vertical]::-ms-track{height:100%}input[type=range].slider::-webkit-slider-runnable-track{cursor:pointer;animate:.2s;box-shadow:0 0 0 #7a7a7a;background:#dbdbdb;border-radius:4px;border:0 solid #7a7a7a}input[type=range].slider::-moz-range-track{cursor:pointer;animate:.2s;box-shadow:0 0 0 #7a7a7a;background:#dbdbdb;border-radius:4px;border:0 solid #7a7a7a}input[type=range].slider::-ms-track{cursor:pointer;animate:.2s;box-shadow:0 0 0 #7a7a7a;background:#dbdbdb;border-radius:4px;border:0 solid #7a7a7a}input[type=range].slider::-ms-fill-lower{background:#dbdbdb;border-radius:4px}input[type=range].slider::-ms-fill-upper{background:#dbdbdb;border-radius:4px}input[type=range].slider::-webkit-slider-thumb{box-shadow:none;border:1px solid #b5b5b5;border-radius:4px;background:#fff;cursor:pointer}input[type=range].slider::-moz-range-thumb{box-shadow:none;border:1px solid #b5b5b5;border-radius:4px;background:#fff;cursor:pointer}input[type=range].slider::-ms-thumb{box-shadow:none;border:1px solid #b5b5b5;border-radius:4px;background:#fff;cursor:pointer}input[type=range].slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none}input[type=range].slider.is-circle::-webkit-slider-thumb{border-radius:290486px}input[type=range].slider.is-circle::-moz-range-thumb{border-radius:290486px}input[type=range].slider.is-circle::-ms-thumb{border-radius:290486px}input[type=range].slider:active::-webkit-slider-thumb{-webkit-transform:scale(1.25);transform:scale(1.25)}input[type=range].slider:active::-moz-range-thumb{transform:scale(1.25)}input[type=range].slider:active::-ms-thumb{transform:scale(1.25)}input[type=range].slider:disabled{opacity:.5;cursor:not-allowed}input[type=range].slider:disabled::-webkit-slider-thumb{cursor:not-allowed;-webkit-transform:scale(1);transform:scale(1)}input[type=range].slider:disabled::-moz-range-thumb{cursor:not-allowed;transform:scale(1)}input[type=range].slider:disabled::-ms-thumb{cursor:not-allowed;transform:scale(1)}input[type=range].slider:not([orient=vertical]){min-height:calc((1rem + 2px) * 1.25)}input[type=range].slider:not([orient=vertical])::-webkit-slider-runnable-track{height:.5rem}input[type=range].slider:not([orient=vertical])::-moz-range-track{height:.5rem}input[type=range].slider:not([orient=vertical])::-ms-track{height:.5rem}input[type=range].slider[orient=vertical]::-webkit-slider-runnable-track{width:.5rem}input[type=range].slider[orient=vertical]::-moz-range-track{width:.5rem}input[type=range].slider[orient=vertical]::-ms-track{width:.5rem}input[type=range].slider::-webkit-slider-thumb{height:1rem;width:1rem}input[type=range].slider::-moz-range-thumb{height:1rem;width:1rem}input[type=range].slider::-ms-thumb{height:1rem;width:1rem}input[type=range].slider::-ms-thumb{margin-top:0}input[type=range].slider::-webkit-slider-thumb{margin-top:-.25rem}input[type=range].slider[orient=vertical]::-webkit-slider-thumb{margin-top:auto;margin-left:-.25rem}input[type=range].slider.is-small:not([orient=vertical]){min-height:calc((.75rem + 2px) * 1.25)}input[type=range].slider.is-small:not([orient=vertical])::-webkit-slider-runnable-track{height:.375rem}input[type=range].slider.is-small:not([orient=vertical])::-moz-range-track{height:.375rem}input[type=range].slider.is-small:not([orient=vertical])::-ms-track{height:.375rem}input[type=range].slider.is-small[orient=vertical]::-webkit-slider-runnable-track{width:.375rem}input[type=range].slider.is-small[orient=vertical]::-moz-range-track{width:.375rem}input[type=range].slider.is-small[orient=vertical]::-ms-track{width:.375rem}input[type=range].slider.is-small::-webkit-slider-thumb{height:.75rem;width:.75rem}input[type=range].slider.is-small::-moz-range-thumb{height:.75rem;width:.75rem}input[type=range].slider.is-small::-ms-thumb{height:.75rem;width:.75rem}input[type=range].slider.is-small::-ms-thumb{margin-top:0}input[type=range].slider.is-small::-webkit-slider-thumb{margin-top:-.1875rem}input[type=range].slider.is-small[orient=vertical]::-webkit-slider-thumb{margin-top:auto;margin-left:-.1875rem}input[type=range].slider.is-medium:not([orient=vertical]){min-height:calc((1.25rem + 2px) * 1.25)}input[type=range].slider.is-medium:not([orient=vertical])::-webkit-slider-runnable-track{height:.625rem}input[type=range].slider.is-medium:not([orient=vertical])::-moz-range-track{height:.625rem}input[type=range].slider.is-medium:not([orient=vertical])::-ms-track{height:.625rem}input[type=range].slider.is-medium[orient=vertical]::-webkit-slider-runnable-track{width:.625rem}input[type=range].slider.is-medium[orient=vertical]::-moz-range-track{width:.625rem}input[type=range].slider.is-medium[orient=vertical]::-ms-track{width:.625rem}input[type=range].slider.is-medium::-webkit-slider-thumb{height:1.25rem;width:1.25rem}input[type=range].slider.is-medium::-moz-range-thumb{height:1.25rem;width:1.25rem}input[type=range].slider.is-medium::-ms-thumb{height:1.25rem;width:1.25rem}input[type=range].slider.is-medium::-ms-thumb{margin-top:0}input[type=range].slider.is-medium::-webkit-slider-thumb{margin-top:-.3125rem}input[type=range].slider.is-medium[orient=vertical]::-webkit-slider-thumb{margin-top:auto;margin-left:-.3125rem}input[type=range].slider.is-large:not([orient=vertical]){min-height:calc((1.5rem + 2px) * 1.25)}input[type=range].slider.is-large:not([orient=vertical])::-webkit-slider-runnable-track{height:.75rem}input[type=range].slider.is-large:not([orient=vertical])::-moz-range-track{height:.75rem}input[type=range].slider.is-large:not([orient=vertical])::-ms-track{height:.75rem}input[type=range].slider.is-large[orient=vertical]::-webkit-slider-runnable-track{width:.75rem}input[type=range].slider.is-large[orient=vertical]::-moz-range-track{width:.75rem}input[type=range].slider.is-large[orient=vertical]::-ms-track{width:.75rem}input[type=range].slider.is-large::-webkit-slider-thumb{height:1.5rem;width:1.5rem}input[type=range].slider.is-large::-moz-range-thumb{height:1.5rem;width:1.5rem}input[type=range].slider.is-large::-ms-thumb{height:1.5rem;width:1.5rem}input[type=range].slider.is-large::-ms-thumb{margin-top:0}input[type=range].slider.is-large::-webkit-slider-thumb{margin-top:-.375rem}input[type=range].slider.is-large[orient=vertical]::-webkit-slider-thumb{margin-top:auto;margin-left:-.375rem}input[type=range].slider.is-white::-moz-range-track{background:#fff!important}input[type=range].slider.is-white::-webkit-slider-runnable-track{background:#fff!important}input[type=range].slider.is-white::-ms-track{background:#fff!important}input[type=range].slider.is-white::-ms-fill-lower{background:#fff}input[type=range].slider.is-white::-ms-fill-upper{background:#fff}input[type=range].slider.is-white .has-output-tooltip+output,input[type=range].slider.is-white.has-output+output{background-color:#fff;color:#0a0a0a}input[type=range].slider.is-black::-moz-range-track{background:#0a0a0a!important}input[type=range].slider.is-black::-webkit-slider-runnable-track{background:#0a0a0a!important}input[type=range].slider.is-black::-ms-track{background:#0a0a0a!important}input[type=range].slider.is-black::-ms-fill-lower{background:#0a0a0a}input[type=range].slider.is-black::-ms-fill-upper{background:#0a0a0a}input[type=range].slider.is-black .has-output-tooltip+output,input[type=range].slider.is-black.has-output+output{background-color:#0a0a0a;color:#fff}input[type=range].slider.is-light::-moz-range-track{background:#f5f5f5!important}input[type=range].slider.is-light::-webkit-slider-runnable-track{background:#f5f5f5!important}input[type=range].slider.is-light::-ms-track{background:#f5f5f5!important}input[type=range].slider.is-light::-ms-fill-lower{background:#f5f5f5}input[type=range].slider.is-light::-ms-fill-upper{background:#f5f5f5}input[type=range].slider.is-light .has-output-tooltip+output,input[type=range].slider.is-light.has-output+output{background-color:#f5f5f5;color:#363636}input[type=range].slider.is-dark::-moz-range-track{background:#363636!important}input[type=range].slider.is-dark::-webkit-slider-runnable-track{background:#363636!important}input[type=range].slider.is-dark::-ms-track{background:#363636!important}input[type=range].slider.is-dark::-ms-fill-lower{background:#363636}input[type=range].slider.is-dark::-ms-fill-upper{background:#363636}input[type=range].slider.is-dark .has-output-tooltip+output,input[type=range].slider.is-dark.has-output+output{background-color:#363636;color:#f5f5f5}input[type=range].slider.is-primary::-moz-range-track{background:#00d1b2!important}input[type=range].slider.is-primary::-webkit-slider-runnable-track{background:#00d1b2!important}input[type=range].slider.is-primary::-ms-track{background:#00d1b2!important}input[type=range].slider.is-primary::-ms-fill-lower{background:#00d1b2}input[type=range].slider.is-primary::-ms-fill-upper{background:#00d1b2}input[type=range].slider.is-primary .has-output-tooltip+output,input[type=range].slider.is-primary.has-output+output{background-color:#00d1b2;color:#fff}input[type=range].slider.is-link::-moz-range-track{background:#3273dc!important}input[type=range].slider.is-link::-webkit-slider-runnable-track{background:#3273dc!important}input[type=range].slider.is-link::-ms-track{background:#3273dc!important}input[type=range].slider.is-link::-ms-fill-lower{background:#3273dc}input[type=range].slider.is-link::-ms-fill-upper{background:#3273dc}input[type=range].slider.is-link .has-output-tooltip+output,input[type=range].slider.is-link.has-output+output{background-color:#3273dc;color:#fff}input[type=range].slider.is-info::-moz-range-track{background:#209cee!important}input[type=range].slider.is-info::-webkit-slider-runnable-track{background:#209cee!important}input[type=range].slider.is-info::-ms-track{background:#209cee!important}input[type=range].slider.is-info::-ms-fill-lower{background:#209cee}input[type=range].slider.is-info::-ms-fill-upper{background:#209cee}input[type=range].slider.is-info .has-output-tooltip+output,input[type=range].slider.is-info.has-output+output{background-color:#209cee;color:#fff}input[type=range].slider.is-success::-moz-range-track{background:#23d160!important}input[type=range].slider.is-success::-webkit-slider-runnable-track{background:#23d160!important}input[type=range].slider.is-success::-ms-track{background:#23d160!important}input[type=range].slider.is-success::-ms-fill-lower{background:#23d160}input[type=range].slider.is-success::-ms-fill-upper{background:#23d160}input[type=range].slider.is-success .has-output-tooltip+output,input[type=range].slider.is-success.has-output+output{background-color:#23d160;color:#fff}input[type=range].slider.is-warning::-moz-range-track{background:#ffdd57!important}input[type=range].slider.is-warning::-webkit-slider-runnable-track{background:#ffdd57!important}input[type=range].slider.is-warning::-ms-track{background:#ffdd57!important}input[type=range].slider.is-warning::-ms-fill-lower{background:#ffdd57}input[type=range].slider.is-warning::-ms-fill-upper{background:#ffdd57}input[type=range].slider.is-warning .has-output-tooltip+output,input[type=range].slider.is-warning.has-output+output{background-color:#ffdd57;color:rgba(0,0,0,.7)}input[type=range].slider.is-danger::-moz-range-track{background:#ff3860!important}input[type=range].slider.is-danger::-webkit-slider-runnable-track{background:#ff3860!important}input[type=range].slider.is-danger::-ms-track{background:#ff3860!important}input[type=range].slider.is-danger::-ms-fill-lower{background:#ff3860}input[type=range].slider.is-danger::-ms-fill-upper{background:#ff3860}input[type=range].slider.is-danger .has-output-tooltip+output,input[type=range].slider.is-danger.has-output+output{background-color:#ff3860;color:#fff}
--------------------------------------------------------------------------------
/docs/_site/static/css/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Noto Sans', sans-serif;
3 | }
4 |
5 |
6 | .footer .icon-link {
7 | font-size: 25px;
8 | color: #000;
9 | }
10 |
11 | .link-block a {
12 | margin-top: 5px;
13 | margin-bottom: 5px;
14 | }
15 |
16 | .dnerf {
17 | font-variant: small-caps;
18 | }
19 |
20 |
21 | .teaser .hero-body {
22 | padding-top: 0;
23 | padding-bottom: 3rem;
24 | }
25 |
26 | .teaser {
27 | font-family: 'Google Sans', sans-serif;
28 | }
29 |
30 |
31 | .publication-title {
32 | }
33 |
34 | .publication-banner {
35 | max-height: parent;
36 |
37 | }
38 |
39 | .publication-banner video {
40 | position: relative;
41 | left: auto;
42 | top: auto;
43 | transform: none;
44 | object-fit: fit;
45 | }
46 |
47 | .publication-header .hero-body {
48 | }
49 |
50 | .publication-title {
51 | font-family: 'Google Sans', sans-serif;
52 | }
53 |
54 | .publication-authors {
55 | font-family: 'Google Sans', sans-serif;
56 | }
57 |
58 | .publication-venue {
59 | color: #555;
60 | width: fit-content;
61 | font-weight: bold;
62 | }
63 |
64 | .publication-awards {
65 | color: #ff3860;
66 | width: fit-content;
67 | font-weight: bolder;
68 | }
69 |
70 | .publication-authors {
71 | }
72 |
73 | .publication-authors a {
74 | color: hsl(204, 86%, 53%) !important;
75 | }
76 |
77 | .publication-authors a:hover {
78 | text-decoration: underline;
79 | }
80 |
81 | .author-block {
82 | display: inline-block;
83 | }
84 |
85 | .publication-banner img {
86 | }
87 |
88 | .publication-authors {
89 | /*color: #4286f4;*/
90 | }
91 |
92 | .publication-video {
93 | position: relative;
94 | width: 100%;
95 | height: 0;
96 | padding-bottom: 56.25%;
97 |
98 | overflow: hidden;
99 | border-radius: 10px !important;
100 | }
101 |
102 | .publication-video iframe {
103 | position: absolute;
104 | top: 0;
105 | left: 0;
106 | width: 100%;
107 | height: 100%;
108 | }
109 |
110 | .publication-body img {
111 | }
112 |
113 | .results-carousel {
114 | overflow: hidden;
115 | }
116 |
117 | .results-carousel .item {
118 | margin: 5px;
119 | overflow: hidden;
120 | padding: 20px;
121 | font-size: 0;
122 | }
123 |
124 | .results-carousel video {
125 | margin: 0;
126 | }
127 |
128 | .slider-pagination .slider-page {
129 | background: #000000;
130 | }
131 |
132 | .eql-cntrb {
133 | font-size: smaller;
134 | }
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/docs/_site/static/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/docs/_site/static/images/icon.png
--------------------------------------------------------------------------------
/docs/_site/static/js/bulma-slider.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.bulmaSlider=e():t.bulmaSlider=e()}("undefined"!=typeof self?self:this,function(){return function(n){var r={};function i(t){if(r[t])return r[t].exports;var e=r[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,i),e.l=!0,e.exports}return i.m=n,i.c=r,i.d=function(t,e,n){i.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:n})},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=0)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),n.d(e,"isString",function(){return l});var r=n(1),i=Object.assign||function(t){for(var e=1;e=l.length&&(s=!0)):s=!0),s&&(t.once&&(u[e]=null),t.callback(r))});-1!==u.indexOf(null);)u.splice(u.indexOf(null),1)}}]),e}();e.a=i}]).default});
--------------------------------------------------------------------------------
/docs/_site/static/js/index.js:
--------------------------------------------------------------------------------
1 | window.HELP_IMPROVE_VIDEOJS = false;
2 |
3 |
4 | $(document).ready(function() {
5 | // Check for click events on the navbar burger icon
6 |
7 | var options = {
8 | slidesToScroll: 1,
9 | slidesToShow: 1,
10 | loop: true,
11 | infinite: true,
12 | autoplay: true,
13 | autoplaySpeed: 5000,
14 | }
15 |
16 | // Initialize all div with carousel class
17 | var carousels = bulmaCarousel.attach('.carousel', options);
18 |
19 | bulmaSlider.attach();
20 |
21 | })
22 |
--------------------------------------------------------------------------------
/docs/_site/static/pdfs/sample.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/docs/_site/static/pdfs/sample.pdf
--------------------------------------------------------------------------------
/docs/_site/static/videos/teaser.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/docs/_site/static/videos/teaser.mp4
--------------------------------------------------------------------------------
/docs/static/css/bulma-carousel.min.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.slider{position:relative;width:100%}.slider-container{display:flex;flex-wrap:nowrap;flex-direction:row;overflow:hidden;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);min-height:100%}.slider-container.is-vertical{flex-direction:column}.slider-container .slider-item{flex:none}.slider-container .slider-item .image.is-covered img{-o-object-fit:cover;object-fit:cover;-o-object-position:center center;object-position:center center;height:100%;width:100%}.slider-container .slider-item .video-container{height:0;padding-bottom:0;padding-top:56.25%;margin:0;position:relative}.slider-container .slider-item .video-container.is-1by1,.slider-container .slider-item .video-container.is-square{padding-top:100%}.slider-container .slider-item .video-container.is-4by3{padding-top:75%}.slider-container .slider-item .video-container.is-21by9{padding-top:42.857143%}.slider-container .slider-item .video-container embed,.slider-container .slider-item .video-container iframe,.slider-container .slider-item .video-container object{position:absolute;top:0;left:0;width:100%!important;height:100%!important}.slider-navigation-next,.slider-navigation-previous{display:flex;justify-content:center;align-items:center;position:absolute;width:42px;height:42px;background:#fff center center no-repeat;background-size:20px 20px;border:1px solid #fff;border-radius:25091983px;box-shadow:0 2px 5px #3232321a;top:50%;margin-top:-20px;left:0;cursor:pointer;transition:opacity .3s,-webkit-transform .3s;transition:transform .3s,opacity .3s;transition:transform .3s,opacity .3s,-webkit-transform .3s}.slider-navigation-next:hover,.slider-navigation-previous:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.slider-navigation-next.is-hidden,.slider-navigation-previous.is-hidden{display:none;opacity:0}.slider-navigation-next svg,.slider-navigation-previous svg{width:25%}.slider-navigation-next{left:auto;right:0;background:#fff center center no-repeat;background-size:20px 20px}.slider-pagination{display:none;justify-content:center;align-items:center;position:absolute;bottom:0;left:0;right:0;padding:.5rem 1rem;text-align:center}.slider-pagination .slider-page{background:#fff;width:10px;height:10px;border-radius:25091983px;display:inline-block;margin:0 3px;box-shadow:0 2px 5px #3232321a;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;cursor:pointer}.slider-pagination .slider-page.is-active,.slider-pagination .slider-page:hover{-webkit-transform:scale(1.4);transform:scale(1.4)}@media screen and (min-width:800px){.slider-pagination{display:flex}}.hero.has-carousel{position:relative}.hero.has-carousel+.hero-body,.hero.has-carousel+.hero-footer,.hero.has-carousel+.hero-head{z-index:10;overflow:hidden}.hero.has-carousel .hero-carousel{position:absolute;top:0;left:0;bottom:0;right:0;height:auto;border:none;margin:auto;padding:0;z-index:0}.hero.has-carousel .hero-carousel .slider{width:100%;max-width:100%;overflow:hidden;height:100%!important;max-height:100%;z-index:0}.hero.has-carousel .hero-carousel .slider .has-background{max-height:100%}.hero.has-carousel .hero-carousel .slider .has-background .is-background{-o-object-fit:cover;object-fit:cover;-o-object-position:center center;object-position:center center;height:100%;width:100%}.hero.has-carousel .hero-body{margin:0 3rem;z-index:10}
--------------------------------------------------------------------------------
/docs/static/css/bulma-slider.min.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}input[type=range].slider{-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:1rem 0;background:0 0;touch-action:none}input[type=range].slider.is-fullwidth{display:block;width:100%}input[type=range].slider:focus{outline:0}input[type=range].slider:not([orient=vertical])::-webkit-slider-runnable-track{width:100%}input[type=range].slider:not([orient=vertical])::-moz-range-track{width:100%}input[type=range].slider:not([orient=vertical])::-ms-track{width:100%}input[type=range].slider:not([orient=vertical]).has-output+output,input[type=range].slider:not([orient=vertical]).has-output-tooltip+output{width:3rem;background:#4a4a4a;border-radius:4px;padding:.4rem .8rem;font-size:.75rem;line-height:.75rem;text-align:center;text-overflow:ellipsis;white-space:nowrap;color:#fff;overflow:hidden;pointer-events:none;z-index:200}input[type=range].slider:not([orient=vertical]).has-output-tooltip:disabled+output,input[type=range].slider:not([orient=vertical]).has-output:disabled+output{opacity:.5}input[type=range].slider:not([orient=vertical]).has-output{display:inline-block;vertical-align:middle;width:calc(100% - (4.2rem))}input[type=range].slider:not([orient=vertical]).has-output+output{display:inline-block;margin-left:.75rem;vertical-align:middle}input[type=range].slider:not([orient=vertical]).has-output-tooltip{display:block}input[type=range].slider:not([orient=vertical]).has-output-tooltip+output{position:absolute;left:0;top:-.1rem}input[type=range].slider[orient=vertical]{-webkit-appearance:slider-vertical;-moz-appearance:slider-vertical;appearance:slider-vertical;-webkit-writing-mode:bt-lr;-ms-writing-mode:bt-lr;writing-mode:bt-lr}input[type=range].slider[orient=vertical]::-webkit-slider-runnable-track{height:100%}input[type=range].slider[orient=vertical]::-moz-range-track{height:100%}input[type=range].slider[orient=vertical]::-ms-track{height:100%}input[type=range].slider::-webkit-slider-runnable-track{cursor:pointer;animate:.2s;box-shadow:0 0 0 #7a7a7a;background:#dbdbdb;border-radius:4px;border:0 solid #7a7a7a}input[type=range].slider::-moz-range-track{cursor:pointer;animate:.2s;box-shadow:0 0 0 #7a7a7a;background:#dbdbdb;border-radius:4px;border:0 solid #7a7a7a}input[type=range].slider::-ms-track{cursor:pointer;animate:.2s;box-shadow:0 0 0 #7a7a7a;background:#dbdbdb;border-radius:4px;border:0 solid #7a7a7a}input[type=range].slider::-ms-fill-lower{background:#dbdbdb;border-radius:4px}input[type=range].slider::-ms-fill-upper{background:#dbdbdb;border-radius:4px}input[type=range].slider::-webkit-slider-thumb{box-shadow:none;border:1px solid #b5b5b5;border-radius:4px;background:#fff;cursor:pointer}input[type=range].slider::-moz-range-thumb{box-shadow:none;border:1px solid #b5b5b5;border-radius:4px;background:#fff;cursor:pointer}input[type=range].slider::-ms-thumb{box-shadow:none;border:1px solid #b5b5b5;border-radius:4px;background:#fff;cursor:pointer}input[type=range].slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none}input[type=range].slider.is-circle::-webkit-slider-thumb{border-radius:290486px}input[type=range].slider.is-circle::-moz-range-thumb{border-radius:290486px}input[type=range].slider.is-circle::-ms-thumb{border-radius:290486px}input[type=range].slider:active::-webkit-slider-thumb{-webkit-transform:scale(1.25);transform:scale(1.25)}input[type=range].slider:active::-moz-range-thumb{transform:scale(1.25)}input[type=range].slider:active::-ms-thumb{transform:scale(1.25)}input[type=range].slider:disabled{opacity:.5;cursor:not-allowed}input[type=range].slider:disabled::-webkit-slider-thumb{cursor:not-allowed;-webkit-transform:scale(1);transform:scale(1)}input[type=range].slider:disabled::-moz-range-thumb{cursor:not-allowed;transform:scale(1)}input[type=range].slider:disabled::-ms-thumb{cursor:not-allowed;transform:scale(1)}input[type=range].slider:not([orient=vertical]){min-height:calc((1rem + 2px) * 1.25)}input[type=range].slider:not([orient=vertical])::-webkit-slider-runnable-track{height:.5rem}input[type=range].slider:not([orient=vertical])::-moz-range-track{height:.5rem}input[type=range].slider:not([orient=vertical])::-ms-track{height:.5rem}input[type=range].slider[orient=vertical]::-webkit-slider-runnable-track{width:.5rem}input[type=range].slider[orient=vertical]::-moz-range-track{width:.5rem}input[type=range].slider[orient=vertical]::-ms-track{width:.5rem}input[type=range].slider::-webkit-slider-thumb{height:1rem;width:1rem}input[type=range].slider::-moz-range-thumb{height:1rem;width:1rem}input[type=range].slider::-ms-thumb{height:1rem;width:1rem}input[type=range].slider::-ms-thumb{margin-top:0}input[type=range].slider::-webkit-slider-thumb{margin-top:-.25rem}input[type=range].slider[orient=vertical]::-webkit-slider-thumb{margin-top:auto;margin-left:-.25rem}input[type=range].slider.is-small:not([orient=vertical]){min-height:calc((.75rem + 2px) * 1.25)}input[type=range].slider.is-small:not([orient=vertical])::-webkit-slider-runnable-track{height:.375rem}input[type=range].slider.is-small:not([orient=vertical])::-moz-range-track{height:.375rem}input[type=range].slider.is-small:not([orient=vertical])::-ms-track{height:.375rem}input[type=range].slider.is-small[orient=vertical]::-webkit-slider-runnable-track{width:.375rem}input[type=range].slider.is-small[orient=vertical]::-moz-range-track{width:.375rem}input[type=range].slider.is-small[orient=vertical]::-ms-track{width:.375rem}input[type=range].slider.is-small::-webkit-slider-thumb{height:.75rem;width:.75rem}input[type=range].slider.is-small::-moz-range-thumb{height:.75rem;width:.75rem}input[type=range].slider.is-small::-ms-thumb{height:.75rem;width:.75rem}input[type=range].slider.is-small::-ms-thumb{margin-top:0}input[type=range].slider.is-small::-webkit-slider-thumb{margin-top:-.1875rem}input[type=range].slider.is-small[orient=vertical]::-webkit-slider-thumb{margin-top:auto;margin-left:-.1875rem}input[type=range].slider.is-medium:not([orient=vertical]){min-height:calc((1.25rem + 2px) * 1.25)}input[type=range].slider.is-medium:not([orient=vertical])::-webkit-slider-runnable-track{height:.625rem}input[type=range].slider.is-medium:not([orient=vertical])::-moz-range-track{height:.625rem}input[type=range].slider.is-medium:not([orient=vertical])::-ms-track{height:.625rem}input[type=range].slider.is-medium[orient=vertical]::-webkit-slider-runnable-track{width:.625rem}input[type=range].slider.is-medium[orient=vertical]::-moz-range-track{width:.625rem}input[type=range].slider.is-medium[orient=vertical]::-ms-track{width:.625rem}input[type=range].slider.is-medium::-webkit-slider-thumb{height:1.25rem;width:1.25rem}input[type=range].slider.is-medium::-moz-range-thumb{height:1.25rem;width:1.25rem}input[type=range].slider.is-medium::-ms-thumb{height:1.25rem;width:1.25rem}input[type=range].slider.is-medium::-ms-thumb{margin-top:0}input[type=range].slider.is-medium::-webkit-slider-thumb{margin-top:-.3125rem}input[type=range].slider.is-medium[orient=vertical]::-webkit-slider-thumb{margin-top:auto;margin-left:-.3125rem}input[type=range].slider.is-large:not([orient=vertical]){min-height:calc((1.5rem + 2px) * 1.25)}input[type=range].slider.is-large:not([orient=vertical])::-webkit-slider-runnable-track{height:.75rem}input[type=range].slider.is-large:not([orient=vertical])::-moz-range-track{height:.75rem}input[type=range].slider.is-large:not([orient=vertical])::-ms-track{height:.75rem}input[type=range].slider.is-large[orient=vertical]::-webkit-slider-runnable-track{width:.75rem}input[type=range].slider.is-large[orient=vertical]::-moz-range-track{width:.75rem}input[type=range].slider.is-large[orient=vertical]::-ms-track{width:.75rem}input[type=range].slider.is-large::-webkit-slider-thumb{height:1.5rem;width:1.5rem}input[type=range].slider.is-large::-moz-range-thumb{height:1.5rem;width:1.5rem}input[type=range].slider.is-large::-ms-thumb{height:1.5rem;width:1.5rem}input[type=range].slider.is-large::-ms-thumb{margin-top:0}input[type=range].slider.is-large::-webkit-slider-thumb{margin-top:-.375rem}input[type=range].slider.is-large[orient=vertical]::-webkit-slider-thumb{margin-top:auto;margin-left:-.375rem}input[type=range].slider.is-white::-moz-range-track{background:#fff!important}input[type=range].slider.is-white::-webkit-slider-runnable-track{background:#fff!important}input[type=range].slider.is-white::-ms-track{background:#fff!important}input[type=range].slider.is-white::-ms-fill-lower{background:#fff}input[type=range].slider.is-white::-ms-fill-upper{background:#fff}input[type=range].slider.is-white .has-output-tooltip+output,input[type=range].slider.is-white.has-output+output{background-color:#fff;color:#0a0a0a}input[type=range].slider.is-black::-moz-range-track{background:#0a0a0a!important}input[type=range].slider.is-black::-webkit-slider-runnable-track{background:#0a0a0a!important}input[type=range].slider.is-black::-ms-track{background:#0a0a0a!important}input[type=range].slider.is-black::-ms-fill-lower{background:#0a0a0a}input[type=range].slider.is-black::-ms-fill-upper{background:#0a0a0a}input[type=range].slider.is-black .has-output-tooltip+output,input[type=range].slider.is-black.has-output+output{background-color:#0a0a0a;color:#fff}input[type=range].slider.is-light::-moz-range-track{background:#f5f5f5!important}input[type=range].slider.is-light::-webkit-slider-runnable-track{background:#f5f5f5!important}input[type=range].slider.is-light::-ms-track{background:#f5f5f5!important}input[type=range].slider.is-light::-ms-fill-lower{background:#f5f5f5}input[type=range].slider.is-light::-ms-fill-upper{background:#f5f5f5}input[type=range].slider.is-light .has-output-tooltip+output,input[type=range].slider.is-light.has-output+output{background-color:#f5f5f5;color:#363636}input[type=range].slider.is-dark::-moz-range-track{background:#363636!important}input[type=range].slider.is-dark::-webkit-slider-runnable-track{background:#363636!important}input[type=range].slider.is-dark::-ms-track{background:#363636!important}input[type=range].slider.is-dark::-ms-fill-lower{background:#363636}input[type=range].slider.is-dark::-ms-fill-upper{background:#363636}input[type=range].slider.is-dark .has-output-tooltip+output,input[type=range].slider.is-dark.has-output+output{background-color:#363636;color:#f5f5f5}input[type=range].slider.is-primary::-moz-range-track{background:#00d1b2!important}input[type=range].slider.is-primary::-webkit-slider-runnable-track{background:#00d1b2!important}input[type=range].slider.is-primary::-ms-track{background:#00d1b2!important}input[type=range].slider.is-primary::-ms-fill-lower{background:#00d1b2}input[type=range].slider.is-primary::-ms-fill-upper{background:#00d1b2}input[type=range].slider.is-primary .has-output-tooltip+output,input[type=range].slider.is-primary.has-output+output{background-color:#00d1b2;color:#fff}input[type=range].slider.is-link::-moz-range-track{background:#3273dc!important}input[type=range].slider.is-link::-webkit-slider-runnable-track{background:#3273dc!important}input[type=range].slider.is-link::-ms-track{background:#3273dc!important}input[type=range].slider.is-link::-ms-fill-lower{background:#3273dc}input[type=range].slider.is-link::-ms-fill-upper{background:#3273dc}input[type=range].slider.is-link .has-output-tooltip+output,input[type=range].slider.is-link.has-output+output{background-color:#3273dc;color:#fff}input[type=range].slider.is-info::-moz-range-track{background:#209cee!important}input[type=range].slider.is-info::-webkit-slider-runnable-track{background:#209cee!important}input[type=range].slider.is-info::-ms-track{background:#209cee!important}input[type=range].slider.is-info::-ms-fill-lower{background:#209cee}input[type=range].slider.is-info::-ms-fill-upper{background:#209cee}input[type=range].slider.is-info .has-output-tooltip+output,input[type=range].slider.is-info.has-output+output{background-color:#209cee;color:#fff}input[type=range].slider.is-success::-moz-range-track{background:#23d160!important}input[type=range].slider.is-success::-webkit-slider-runnable-track{background:#23d160!important}input[type=range].slider.is-success::-ms-track{background:#23d160!important}input[type=range].slider.is-success::-ms-fill-lower{background:#23d160}input[type=range].slider.is-success::-ms-fill-upper{background:#23d160}input[type=range].slider.is-success .has-output-tooltip+output,input[type=range].slider.is-success.has-output+output{background-color:#23d160;color:#fff}input[type=range].slider.is-warning::-moz-range-track{background:#ffdd57!important}input[type=range].slider.is-warning::-webkit-slider-runnable-track{background:#ffdd57!important}input[type=range].slider.is-warning::-ms-track{background:#ffdd57!important}input[type=range].slider.is-warning::-ms-fill-lower{background:#ffdd57}input[type=range].slider.is-warning::-ms-fill-upper{background:#ffdd57}input[type=range].slider.is-warning .has-output-tooltip+output,input[type=range].slider.is-warning.has-output+output{background-color:#ffdd57;color:rgba(0,0,0,.7)}input[type=range].slider.is-danger::-moz-range-track{background:#ff3860!important}input[type=range].slider.is-danger::-webkit-slider-runnable-track{background:#ff3860!important}input[type=range].slider.is-danger::-ms-track{background:#ff3860!important}input[type=range].slider.is-danger::-ms-fill-lower{background:#ff3860}input[type=range].slider.is-danger::-ms-fill-upper{background:#ff3860}input[type=range].slider.is-danger .has-output-tooltip+output,input[type=range].slider.is-danger.has-output+output{background-color:#ff3860;color:#fff}
--------------------------------------------------------------------------------
/docs/static/css/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Noto Sans', sans-serif;
3 | }
4 |
5 |
6 | .footer .icon-link {
7 | font-size: 25px;
8 | color: #000;
9 | }
10 |
11 | .link-block a {
12 | margin-top: 5px;
13 | margin-bottom: 5px;
14 | }
15 |
16 | .dnerf {
17 | font-variant: small-caps;
18 | }
19 |
20 |
21 | .teaser .hero-body {
22 | padding-top: 0;
23 | padding-bottom: 3rem;
24 | }
25 |
26 | .teaser {
27 | font-family: 'Google Sans', sans-serif;
28 | }
29 |
30 |
31 | .publication-title {
32 | }
33 |
34 | .publication-banner {
35 | max-height: parent;
36 |
37 | }
38 |
39 | .publication-banner video {
40 | position: relative;
41 | left: auto;
42 | top: auto;
43 | transform: none;
44 | object-fit: fit;
45 | }
46 |
47 | .publication-header .hero-body {
48 | }
49 |
50 | .publication-title {
51 | font-family: 'Google Sans', sans-serif;
52 | }
53 |
54 | .publication-authors {
55 | font-family: 'Google Sans', sans-serif;
56 | }
57 |
58 | .publication-venue {
59 | color: #555;
60 | width: fit-content;
61 | font-weight: bold;
62 | }
63 |
64 | .publication-awards {
65 | color: #ff3860;
66 | width: fit-content;
67 | font-weight: bolder;
68 | }
69 |
70 | .publication-authors {
71 | }
72 |
73 | .publication-authors a {
74 | color: hsl(204, 86%, 53%) !important;
75 | }
76 |
77 | .publication-authors a:hover {
78 | text-decoration: underline;
79 | }
80 |
81 | .author-block {
82 | display: inline-block;
83 | }
84 |
85 | .publication-banner img {
86 | }
87 |
88 | .publication-authors {
89 | /*color: #4286f4;*/
90 | }
91 |
92 | .publication-video {
93 | position: relative;
94 | width: 100%;
95 | height: 0;
96 | padding-bottom: 56.25%;
97 |
98 | overflow: hidden;
99 | border-radius: 10px !important;
100 | }
101 |
102 | .publication-video iframe {
103 | position: absolute;
104 | top: 0;
105 | left: 0;
106 | width: 100%;
107 | height: 100%;
108 | }
109 |
110 | .publication-body img {
111 | }
112 |
113 | .results-carousel {
114 | overflow: hidden;
115 | }
116 |
117 | .results-carousel .item {
118 | margin: 5px;
119 | overflow: hidden;
120 | padding: 20px;
121 | font-size: 0;
122 | }
123 |
124 | .results-carousel video {
125 | margin: 0;
126 | }
127 |
128 | .slider-pagination .slider-page {
129 | background: #000000;
130 | }
131 |
132 | .eql-cntrb {
133 | font-size: smaller;
134 | }
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/docs/static/images/comp_attention.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/docs/static/images/comp_attention.png
--------------------------------------------------------------------------------
/docs/static/images/comp_flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/docs/static/images/comp_flow.png
--------------------------------------------------------------------------------
/docs/static/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/docs/static/images/icon.png
--------------------------------------------------------------------------------
/docs/static/images/teaser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/docs/static/images/teaser.png
--------------------------------------------------------------------------------
/docs/static/images/teaser_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/docs/static/images/teaser_v2.png
--------------------------------------------------------------------------------
/docs/static/js/bulma-slider.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.bulmaSlider=e():t.bulmaSlider=e()}("undefined"!=typeof self?self:this,function(){return function(n){var r={};function i(t){if(r[t])return r[t].exports;var e=r[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,i),e.l=!0,e.exports}return i.m=n,i.c=r,i.d=function(t,e,n){i.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:n})},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=0)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),n.d(e,"isString",function(){return l});var r=n(1),i=Object.assign||function(t){for(var e=1;e=l.length&&(s=!0)):s=!0),s&&(t.once&&(u[e]=null),t.callback(r))});-1!==u.indexOf(null);)u.splice(u.indexOf(null),1)}}]),e}();e.a=i}]).default});
--------------------------------------------------------------------------------
/docs/static/js/index.js:
--------------------------------------------------------------------------------
1 | window.HELP_IMPROVE_VIDEOJS = false;
2 |
3 |
4 | $(document).ready(function() {
5 | // Check for click events on the navbar burger icon
6 |
7 | var options = {
8 | slidesToScroll: 1,
9 | slidesToShow: 1,
10 | loop: true,
11 | infinite: true,
12 | autoplay: true,
13 | autoplaySpeed: 5000,
14 | }
15 |
16 | // Initialize all div with carousel class
17 | var carousels = bulmaCarousel.attach('.carousel', options);
18 |
19 | bulmaSlider.attach();
20 |
21 | })
22 |
--------------------------------------------------------------------------------
/docs/static/pdfs/sample.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/docs/static/pdfs/sample.pdf
--------------------------------------------------------------------------------
/docs/static/videos/teaser.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/docs/static/videos/teaser.mp4
--------------------------------------------------------------------------------
/docs/static/videos/teaser_v2.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/docs/static/videos/teaser_v2.mp4
--------------------------------------------------------------------------------
/models/flow_2E.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | from .network_utils import *
4 | import torch.nn.functional as F
5 |
6 | class BottleneckBlock(nn.Module):
7 | def __init__(self, in_planes, planes, norm_fn='group', stride=1):
8 | super(BottleneckBlock, self).__init__()
9 |
10 | self.conv1 = nn.Conv2d(in_planes, planes//4, kernel_size=1, padding=0)
11 | self.conv2 = nn.Conv2d(planes//4, planes//4, kernel_size=3, padding=1, stride=stride)
12 | self.conv3 = nn.Conv2d(planes//4, planes, kernel_size=1, padding=0)
13 | self.relu = nn.ReLU(inplace=True)
14 |
15 | num_groups = planes // 8
16 |
17 | if norm_fn == 'group':
18 | self.norm1 = nn.GroupNorm(num_groups=num_groups, num_channels=planes//4)
19 | self.norm2 = nn.GroupNorm(num_groups=num_groups, num_channels=planes//4)
20 | self.norm3 = nn.GroupNorm(num_groups=num_groups, num_channels=planes)
21 | if not stride == 1:
22 | self.norm4 = nn.GroupNorm(num_groups=num_groups, num_channels=planes)
23 |
24 | elif norm_fn == 'batch':
25 | self.norm1 = nn.BatchNorm2d(planes//4)
26 | self.norm2 = nn.BatchNorm2d(planes//4)
27 | self.norm3 = nn.BatchNorm2d(planes)
28 | if not stride == 1:
29 | self.norm4 = nn.BatchNorm2d(planes)
30 |
31 | elif norm_fn == 'instance':
32 | self.norm1 = nn.InstanceNorm2d(planes//4)
33 | self.norm2 = nn.InstanceNorm2d(planes//4)
34 | self.norm3 = nn.InstanceNorm2d(planes)
35 | if not stride == 1:
36 | self.norm4 = nn.InstanceNorm2d(planes)
37 |
38 | elif norm_fn == 'none':
39 | self.norm1 = nn.Sequential()
40 | self.norm2 = nn.Sequential()
41 | self.norm3 = nn.Sequential()
42 | if not stride == 1:
43 | self.norm4 = nn.Sequential()
44 |
45 | if stride == 1:
46 | self.downsample = None
47 |
48 | else:
49 | self.downsample = nn.Sequential(
50 | nn.Conv2d(in_planes, planes, kernel_size=1, stride=stride), self.norm4)
51 |
52 |
53 | def forward(self, x):
54 | y = x
55 | y = self.relu(self.norm1(self.conv1(y)))
56 | y = self.relu(self.norm2(self.conv2(y)))
57 | y = self.relu(self.norm3(self.conv3(y)))
58 |
59 | if self.downsample is not None:
60 | x = self.downsample(x)
61 |
62 | return self.relu(x+y)
63 |
64 |
65 | class Flow_Net(nn.Module):
66 | """ for 2-exposure """
67 | def __init__(self):
68 | super(Flow_Net, self).__init__()
69 |
70 | self.norm_fn = 'batch'
71 | self.in_planes = 32
72 | self.conv1 = nn.Sequential(nn.Conv2d(9, 32, kernel_size=7, stride=2, padding=3),
73 | nn.BatchNorm2d(32),
74 | nn.ReLU(inplace=True))
75 |
76 | self.imconv4 = nn.Sequential(nn.Conv2d(9, 64, kernel_size=3, stride=1, padding=1),
77 | nn.BatchNorm2d(64),
78 | nn.ReLU(inplace=True))
79 |
80 | self.combine4 = nn.Sequential(nn.Conv2d(64*2, 64, kernel_size=1, stride=1, padding=0),
81 | nn.BatchNorm2d(64),
82 | nn.ReLU(inplace=True),
83 | nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
84 | nn.BatchNorm2d(64),
85 | nn.ReLU(inplace=True))
86 |
87 | self.imconv8 = nn.Sequential(nn.Conv2d(9, 128, kernel_size=3, stride=1, padding=1),
88 | nn.BatchNorm2d(128),
89 | nn.ReLU(inplace=True))
90 |
91 | self.combine8 = nn.Sequential(nn.Conv2d(128*2, 128, kernel_size=1, stride=1, padding=0),
92 | nn.BatchNorm2d(128),
93 | nn.ReLU(inplace=True),
94 | nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
95 | nn.BatchNorm2d(128),
96 | nn.ReLU(inplace=True))
97 |
98 | self.imconv16 = nn.Sequential(nn.Conv2d(9, 256, kernel_size=3, stride=1, padding=1),
99 | nn.BatchNorm2d(256),
100 | nn.ReLU(inplace=True))
101 |
102 | self.combine16 = nn.Sequential(nn.Conv2d(256*2, 256, kernel_size=1, stride=1, padding=0),
103 | nn.BatchNorm2d(256),
104 | nn.ReLU(inplace=True),
105 | nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
106 | nn.BatchNorm2d(256),
107 | nn.ReLU(inplace=True))
108 |
109 | self.layer1 = self._make_layer(32, stride=1) # 1/2
110 | self.layer2 = self._make_layer(64, stride=2) # 1/4
111 | self.layer3 = self._make_layer(128, stride=2) # 1/8
112 | self.layer4 = self._make_layer(256, stride=2) # 1/16
113 |
114 | self.upconv4 = nn.Sequential(nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1),
115 | nn.BatchNorm2d(128),
116 | nn.ReLU(inplace=True))
117 |
118 | self.iconv3 = nn.Sequential(nn.Conv2d(128*2, 128, kernel_size=1, stride=1, padding=0),
119 | nn.BatchNorm2d(128),
120 | nn.ReLU(inplace=True),
121 | nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
122 | nn.BatchNorm2d(128),
123 | nn.ReLU(inplace=True))
124 |
125 | self.upconv3 = nn.Sequential(nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1),
126 | nn.BatchNorm2d(64),
127 | nn.ReLU(inplace=True))
128 |
129 | self.iconv2 = nn.Sequential(nn.Conv2d(64*2, 64, kernel_size=1, stride=1, padding=0),
130 | nn.BatchNorm2d(64),
131 | nn.ReLU(inplace=True),
132 | nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
133 | nn.BatchNorm2d(64),
134 | nn.ReLU(inplace=True))
135 |
136 | self.conv7x7 = nn.Conv2d(256, 256, 7, stride=1, padding=7//2, groups=256)
137 | self.conv9x9 = nn.Conv2d(256, 256, 9, stride=1, padding=9//2, groups=256)
138 | self.conv11x11 = nn.Conv2d(256, 256, 11, stride=1, padding=11//2, groups=256)
139 |
140 | self.merge_features = nn.Conv2d(256*3, 256, 1, stride=1, padding=0)
141 |
142 | self.flow_head = nn.Sequential(nn.Conv2d(64, 64, kernel_size=5, stride=1, padding=2),
143 | nn.ReLU(inplace=True),
144 | nn.Conv2d(64, 64, kernel_size=5, stride=1, padding=2),
145 | nn.ReLU(inplace=True),
146 | nn.Conv2d(64, 4, kernel_size=5, stride=1, padding=2))
147 |
148 | def _make_layer(self, dim, stride=1):
149 | layer1 = BottleneckBlock(self.in_planes, dim, self.norm_fn, stride=stride)
150 | layer2 = BottleneckBlock(dim, dim, self.norm_fn, stride=1)
151 | layers = (layer1, layer2)
152 |
153 | self.in_planes = dim
154 | return nn.Sequential(*layers)
155 |
156 |
157 | def forward(self, ldrs, expos):
158 | prev, cur, nxt = ldrs
159 | p_exp, c_exp, n_exp = expos
160 | cur_adj_exp = adj_expo_ldr_to_ldr(cur, c_exp, p_exp)
161 |
162 | p4 = F.avg_pool2d(prev, 4)
163 | c4 = F.avg_pool2d(cur_adj_exp, 4)
164 | n4 = F.avg_pool2d(nxt, 4)
165 |
166 | p8 = F.avg_pool2d(prev, 8)
167 | c8 = F.avg_pool2d(cur_adj_exp, 8)
168 | n8 = F.avg_pool2d(nxt, 8)
169 |
170 | p16 = F.avg_pool2d(prev, 16)
171 | c16 = F.avg_pool2d(cur_adj_exp, 16)
172 | n16 = F.avg_pool2d(nxt, 16)
173 |
174 | x = torch.cat((prev, cur_adj_exp, nxt), dim=1)
175 |
176 | x2 = self.conv1(x)
177 | x2 = self.layer1(x2)
178 | x4 = self.layer2(x2)
179 | x4_ = self.imconv4(torch.cat((p4, c4, n4), dim=1))
180 | x4 = self.combine4(torch.cat((x4, x4_), dim=1))
181 |
182 | x8 = self.layer3(x4)
183 | x8_ = self.imconv8(torch.cat((p8, c8, n8), dim=1))
184 | x8 = self.combine8(torch.cat((x8, x8_), dim=1))
185 |
186 | x16 = self.layer4(x8)
187 | x16_ = self.imconv16(torch.cat((p16, c16, n16), dim=1))
188 | x16 = self.combine16(torch.cat((x16, x16_), dim=1))
189 |
190 | x16_7x7 = self.conv7x7(x16)
191 | x16_9x9 = self.conv9x9(x16)
192 | x16_11x11 = self.conv11x11(x16)
193 | lk_features = torch.cat((x16_7x7, x16_9x9, x16_11x11), dim=1)
194 | lk_features = self.merge_features(lk_features)
195 | x16 = F.relu(x16+lk_features)
196 |
197 | x16_up = self.upconv4(x16)
198 | x8 = self.iconv3(torch.cat((x8, x16_up), dim=1))
199 |
200 | x8_up = self.upconv3(x8)
201 | x4 = self.iconv2(torch.cat((x4, x8_up), dim=1))
202 | flow4 = self.flow_head(x4)
203 |
204 | p_flow4 = flow4[:,0:2].clamp(-100, 100)
205 | n_flow4 = flow4[:,2:].clamp(-100, 100)
206 |
207 | p_flow = F.interpolate(p_flow4, scale_factor=4, mode='bilinear', align_corners=True) * 4.0
208 | n_flow = F.interpolate(n_flow4, scale_factor=4, mode='bilinear', align_corners=True) * 4.0
209 |
210 | return [p_flow, n_flow]
--------------------------------------------------------------------------------
/models/flow_3E.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | from .network_utils import *
4 | import torch.nn.functional as F
5 |
6 | class BottleneckBlock(nn.Module):
7 | def __init__(self, in_planes, planes, norm_fn='group', stride=1):
8 | super(BottleneckBlock, self).__init__()
9 |
10 | self.conv1 = nn.Conv2d(in_planes, planes//4, kernel_size=1, padding=0)
11 | self.conv2 = nn.Conv2d(planes//4, planes//4, kernel_size=3, padding=1, stride=stride)
12 | self.conv3 = nn.Conv2d(planes//4, planes, kernel_size=1, padding=0)
13 | self.relu = nn.ReLU(inplace=True)
14 |
15 | num_groups = planes // 8
16 |
17 | if norm_fn == 'group':
18 | self.norm1 = nn.GroupNorm(num_groups=num_groups, num_channels=planes//4)
19 | self.norm2 = nn.GroupNorm(num_groups=num_groups, num_channels=planes//4)
20 | self.norm3 = nn.GroupNorm(num_groups=num_groups, num_channels=planes)
21 | if not stride == 1:
22 | self.norm4 = nn.GroupNorm(num_groups=num_groups, num_channels=planes)
23 |
24 | elif norm_fn == 'batch':
25 | self.norm1 = nn.BatchNorm2d(planes//4)
26 | self.norm2 = nn.BatchNorm2d(planes//4)
27 | self.norm3 = nn.BatchNorm2d(planes)
28 | if not stride == 1:
29 | self.norm4 = nn.BatchNorm2d(planes)
30 |
31 | elif norm_fn == 'instance':
32 | self.norm1 = nn.InstanceNorm2d(planes//4)
33 | self.norm2 = nn.InstanceNorm2d(planes//4)
34 | self.norm3 = nn.InstanceNorm2d(planes)
35 | if not stride == 1:
36 | self.norm4 = nn.InstanceNorm2d(planes)
37 |
38 | elif norm_fn == 'none':
39 | self.norm1 = nn.Sequential()
40 | self.norm2 = nn.Sequential()
41 | self.norm3 = nn.Sequential()
42 | if not stride == 1:
43 | self.norm4 = nn.Sequential()
44 |
45 | if stride == 1:
46 | self.downsample = None
47 |
48 | else:
49 | self.downsample = nn.Sequential(
50 | nn.Conv2d(in_planes, planes, kernel_size=1, stride=stride), self.norm4)
51 |
52 |
53 | def forward(self, x):
54 | y = x
55 | y = self.relu(self.norm1(self.conv1(y)))
56 | y = self.relu(self.norm2(self.conv2(y)))
57 | y = self.relu(self.norm3(self.conv3(y)))
58 |
59 | if self.downsample is not None:
60 | x = self.downsample(x)
61 |
62 | return self.relu(x+y)
63 |
64 |
65 | class Flow_Net(nn.Module):
66 | """ for 2-exposure """
67 | def __init__(self):
68 | super(Flow_Net, self).__init__()
69 |
70 | self.norm_fn = 'batch'
71 | self.in_planes = 32
72 | self.conv1 = nn.Sequential(nn.Conv2d(18, 32, kernel_size=7, stride=2, padding=3),
73 | nn.BatchNorm2d(32),
74 | nn.ReLU(inplace=True))
75 |
76 | self.imconv4 = nn.Sequential(nn.Conv2d(18, 64, kernel_size=3, stride=1, padding=1),
77 | nn.BatchNorm2d(64),
78 | nn.ReLU(inplace=True))
79 |
80 | self.combine4 = nn.Sequential(nn.Conv2d(64*2, 64, kernel_size=1, stride=1, padding=0),
81 | nn.BatchNorm2d(64),
82 | nn.ReLU(inplace=True),
83 | nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
84 | nn.BatchNorm2d(64),
85 | nn.ReLU(inplace=True))
86 |
87 | self.imconv8 = nn.Sequential(nn.Conv2d(18, 128, kernel_size=3, stride=1, padding=1),
88 | nn.BatchNorm2d(128),
89 | nn.ReLU(inplace=True))
90 |
91 | self.combine8 = nn.Sequential(nn.Conv2d(128*2, 128, kernel_size=1, stride=1, padding=0),
92 | nn.BatchNorm2d(128),
93 | nn.ReLU(inplace=True),
94 | nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
95 | nn.BatchNorm2d(128),
96 | nn.ReLU(inplace=True))
97 |
98 | self.imconv16 = nn.Sequential(nn.Conv2d(18, 256, kernel_size=3, stride=1, padding=1),
99 | nn.BatchNorm2d(256),
100 | nn.ReLU(inplace=True))
101 |
102 | self.combine16 = nn.Sequential(nn.Conv2d(256*2, 256, kernel_size=1, stride=1, padding=0),
103 | nn.BatchNorm2d(256),
104 | nn.ReLU(inplace=True),
105 | nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
106 | nn.BatchNorm2d(256),
107 | nn.ReLU(inplace=True))
108 |
109 | self.layer1 = self._make_layer(32, stride=1) # 1/2
110 | self.layer2 = self._make_layer(64, stride=2) # 1/4
111 | self.layer3 = self._make_layer(128, stride=2) # 1/8
112 | self.layer4 = self._make_layer(256, stride=2) # 1/16
113 |
114 | self.upconv4 = nn.Sequential(nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1),
115 | nn.BatchNorm2d(128),
116 | nn.ReLU(inplace=True))
117 |
118 | self.iconv3 = nn.Sequential(nn.Conv2d(128*2, 128, kernel_size=1, stride=1, padding=0),
119 | nn.BatchNorm2d(128),
120 | nn.ReLU(inplace=True),
121 | nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
122 | nn.BatchNorm2d(128),
123 | nn.ReLU(inplace=True))
124 |
125 | self.upconv3 = nn.Sequential(nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1),
126 | nn.BatchNorm2d(64),
127 | nn.ReLU(inplace=True))
128 |
129 | self.iconv2 = nn.Sequential(nn.Conv2d(64*2, 64, kernel_size=1, stride=1, padding=0),
130 | nn.BatchNorm2d(64),
131 | nn.ReLU(inplace=True),
132 | nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
133 | nn.BatchNorm2d(64),
134 | nn.ReLU(inplace=True))
135 |
136 | self.conv7x7 = nn.Conv2d(256, 256, 7, stride=1, padding=7//2, groups=256)
137 | self.conv9x9 = nn.Conv2d(256, 256, 9, stride=1, padding=9//2, groups=256)
138 | self.conv11x11 = nn.Conv2d(256, 256, 11, stride=1, padding=11//2, groups=256)
139 |
140 | self.merge_features = nn.Conv2d(256*3, 256, 1, stride=1, padding=0)
141 |
142 | self.flow_head = nn.Sequential(nn.Conv2d(64, 128, kernel_size=5, stride=1, padding=2),
143 | nn.ReLU(inplace=True),
144 | nn.Conv2d(128, 128, kernel_size=5, stride=1, padding=2),
145 | nn.ReLU(inplace=True),
146 | nn.Conv2d(128, 8, kernel_size=5, stride=1, padding=2))
147 |
148 | def _make_layer(self, dim, stride=1):
149 | layer1 = BottleneckBlock(self.in_planes, dim, self.norm_fn, stride=stride)
150 | layer2 = BottleneckBlock(dim, dim, self.norm_fn, stride=1)
151 | layers = (layer1, layer2)
152 |
153 | self.in_planes = dim
154 | return nn.Sequential(*layers)
155 |
156 |
157 | def forward(self, ldrs, expos):
158 |
159 | p2, p1, c, n1, n2 = ldrs
160 | p2_exp, p1_exp, c_exp, n1_exp, n2_exp = expos
161 | c_adj_p2 = adj_expo_ldr_to_ldr(c, c_exp, p2_exp)
162 | c_adj_p1 = adj_expo_ldr_to_ldr(c, c_exp, p1_exp)
163 |
164 | p2_4 = F.avg_pool2d(p2, 4)
165 | c_adj_p2_4 = F.avg_pool2d(c_adj_p2, 4)
166 | n1_4 = F.avg_pool2d(n1, 4)
167 |
168 | p1_4 = F.avg_pool2d(p1, 4)
169 | c_adj_p1_4 = F.avg_pool2d(c_adj_p1, 4)
170 | n2_4 = F.avg_pool2d(n2, 4)
171 |
172 | p2_8 = F.avg_pool2d(p2, 8)
173 | c_adj_p2_8 = F.avg_pool2d(c_adj_p2, 8)
174 | n1_8 = F.avg_pool2d(n1, 8)
175 |
176 | p1_8 = F.avg_pool2d(p1, 8)
177 | c_adj_p1_8 = F.avg_pool2d(c_adj_p1, 8)
178 | n2_8 = F.avg_pool2d(n2, 8)
179 |
180 | p2_16 = F.avg_pool2d(p2, 16)
181 | c_adj_p2_16 = F.avg_pool2d(c_adj_p2, 16)
182 | n1_16 = F.avg_pool2d(n1, 16)
183 |
184 | p1_16 = F.avg_pool2d(p1, 16)
185 | c_adj_p1_16 = F.avg_pool2d(c_adj_p1, 16)
186 | n2_16 = F.avg_pool2d(n2, 16)
187 |
188 | x = torch.cat((p2, c_adj_p2, n1, p1, c_adj_p1, n2), dim=1)
189 |
190 | x2 = self.conv1(x)
191 | x2 = self.layer1(x2)
192 | x4 = self.layer2(x2)
193 | x4_ = self.imconv4(torch.cat((p2_4, c_adj_p2_4, n1_4, p1_4, c_adj_p1_4, n2_4), dim=1))
194 | x4 = self.combine4(torch.cat((x4, x4_), dim=1))
195 |
196 | x8 = self.layer3(x4)
197 | x8_ = self.imconv8(torch.cat((p2_8, c_adj_p2_8, n1_8, p1_8, c_adj_p1_8, n2_8), dim=1))
198 | x8 = self.combine8(torch.cat((x8, x8_), dim=1))
199 |
200 | x16 = self.layer4(x8)
201 | x16_ = self.imconv16(torch.cat((p2_16, c_adj_p2_16, n1_16, p1_16, c_adj_p1_16, n2_16), dim=1))
202 | x16 = self.combine16(torch.cat((x16, x16_), dim=1))
203 | # x16 = self.conv2(x16)
204 |
205 | x16_7x7 = self.conv7x7(x16)
206 | x16_9x9 = self.conv9x9(x16)
207 | x16_11x11 = self.conv11x11(x16)
208 | lk_features = torch.cat((x16_7x7, x16_9x9, x16_11x11), dim=1)
209 | lk_features = self.merge_features(lk_features)
210 | x16 = F.relu(x16+lk_features)
211 |
212 | x16_up = self.upconv4(x16)
213 | x8 = self.iconv3(torch.cat((x8, x16_up), dim=1))
214 |
215 | x8_up = self.upconv3(x8)
216 | x4 = self.iconv2(torch.cat((x4, x8_up), dim=1))
217 | flow4 = self.flow_head(x4)
218 |
219 | p2_flow4 = flow4[:,:2].clamp(-64, 64)
220 | n1_flow4 = flow4[:,2:4].clamp(-64, 64)
221 |
222 | p1_flow4 = flow4[:,4:6].clamp(-64, 64)
223 | n2_flow4 = flow4[:,6:].clamp(-64, 64)
224 |
225 | p2_flow = F.interpolate(p2_flow4, scale_factor=4, mode='bilinear', align_corners=True) * 4.0
226 | n1_flow = F.interpolate(n1_flow4, scale_factor=4, mode='bilinear', align_corners=True) * 4.0
227 |
228 | p1_flow = F.interpolate(p1_flow4, scale_factor=4, mode='bilinear', align_corners=True) * 4.0
229 | n2_flow = F.interpolate(n2_flow4, scale_factor=4, mode='bilinear', align_corners=True) * 4.0
230 |
231 | return [p2_flow, n1_flow, p1_flow, n2_flow]
--------------------------------------------------------------------------------
/models/fusion_2E.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | from .network_utils import *
4 |
5 | class Downsample(nn.Module):
6 | def __init__(self, c_in, c_out=512, use_bn=True, afunc='LReLU'):
7 | super(Downsample, self).__init__()
8 |
9 | self.layers = torch.nn.Sequential(
10 | conv_layer(c_in, c_out//4, k=3, stride=2, pad=1, afunc=afunc, use_bn=use_bn),
11 | conv_layer(c_out//4, c_out//4, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn),
12 | conv_layer(c_out//4, c_out//2, k=3, stride=2, pad=1, afunc=afunc, use_bn=use_bn),
13 | conv_layer(c_out//2, c_out//2, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn),
14 | conv_layer(c_out//2, c_out, k=3, stride=2, pad=1, afunc=afunc, use_bn=use_bn),
15 | conv_layer(c_out, c_out, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn),
16 | )
17 |
18 | def forward(self, inputs):
19 | out = self.layers(inputs)
20 | return out
21 |
22 | class Upsample(nn.Module):
23 | def __init__(self, c_in, c_out, use_bn=True, afunc='LReLU'):
24 | super(Upsample, self).__init__()
25 | last_c = max(128, c_in // 8)
26 | self.layers = torch.nn.Sequential(
27 | deconv_layer(c_in, c_in//2, use_bn=use_bn, afunc=afunc),
28 | conv_layer(c_in//2, c_in//2, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn),
29 | deconv_layer(c_in//2, c_in//2, use_bn=use_bn, afunc=afunc),
30 | conv_layer(c_in//2, c_in//2, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn),
31 | deconv_layer(c_in//2, last_c, use_bn=use_bn, afunc=afunc),
32 | output_conv(last_c, c_out, k=3, stride=1, pad=1),
33 | )
34 |
35 | def forward(self, inputs):
36 | out = self.layers(inputs)
37 | out = torch.sigmoid(out)
38 | return out
39 |
40 | class Fusion_Net(nn.Module):
41 | def __init__(self, c_in=30, c_out=15, c_mid=256):
42 | super(Fusion_Net, self).__init__()
43 | self.conv1 = torch.nn.Sequential(
44 | nn.Conv2d(c_in, c_mid//4, kernel_size=3, stride=2, padding=1),
45 | nn.BatchNorm2d(c_mid//4),
46 | nn.LeakyReLU(0.1, inplace=True))
47 | self.conv2 = torch.nn.Sequential(
48 | nn.Conv2d(c_mid//4, c_mid//4, kernel_size=3, stride=1, padding=1),
49 | nn.BatchNorm2d(c_mid//4),
50 | nn.LeakyReLU(0.1, inplace=True))
51 | self.conv3 = torch.nn.Sequential(
52 | nn.Conv2d(c_mid//4, c_mid//2, kernel_size=3, stride=2, padding=1),
53 | nn.BatchNorm2d(c_mid//2),
54 | nn.LeakyReLU(0.1, inplace=True))
55 | self.conv4 = torch.nn.Sequential(
56 | nn.Conv2d(c_mid//2, c_mid//2, kernel_size=3, stride=1, padding=1),
57 | nn.BatchNorm2d(c_mid//2),
58 | nn.LeakyReLU(0.1, inplace=True))
59 |
60 | self.conv5 = torch.nn.Sequential(
61 | nn.Conv2d(c_mid//2, c_mid, kernel_size=3, stride=2, padding=1),
62 | nn.BatchNorm2d(c_mid),
63 | nn.LeakyReLU(0.1, inplace=True))
64 | self.conv6 = torch.nn.Sequential(
65 | nn.Conv2d(c_mid, c_mid, kernel_size=3, stride=1, padding=1),
66 | nn.BatchNorm2d(c_mid),
67 | nn.LeakyReLU(0.1, inplace=True))
68 |
69 | self.conv7 = torch.nn.Sequential(
70 | nn.ConvTranspose2d(c_mid, c_mid//2, kernel_size=4, stride=2, padding=1),
71 | nn.BatchNorm2d(c_mid//2),
72 | nn.LeakyReLU(0.1, inplace=True))
73 | self.conv8 = torch.nn.Sequential(
74 | nn.Conv2d(c_mid, c_mid//2, kernel_size=3, stride=1, padding=1),
75 | nn.BatchNorm2d(c_mid//2),
76 | nn.LeakyReLU(0.1, inplace=True))
77 | self.conv9 = torch.nn.Sequential(
78 | nn.ConvTranspose2d(c_mid//2, c_mid//2, kernel_size=4, stride=2, padding=1),
79 | nn.BatchNorm2d(c_mid//2),
80 | nn.LeakyReLU(0.1, inplace=True))
81 | self.conv10 = torch.nn.Sequential(
82 | nn.Conv2d(c_mid//2+c_mid//4, c_mid//2, kernel_size=3, stride=1, padding=1),
83 | nn.BatchNorm2d(c_mid//2),
84 | nn.LeakyReLU(0.1, inplace=True))
85 |
86 | self.conv11 = torch.nn.Sequential(
87 | nn.ConvTranspose2d(c_mid//2, 64, kernel_size=4, stride=2, padding=1),
88 | nn.BatchNorm2d(64),
89 | nn.LeakyReLU(0.1, inplace=True))
90 |
91 | self.conv12 = torch.nn.Sequential(
92 | nn.Conv2d(64, c_out, kernel_size=3, stride=1, padding=1))
93 |
94 |
95 | self.merge_HDR = MergeHDRModule()
96 |
97 | def forward(self, inputs, hdrs):
98 | pred = OrderedDict()
99 | n, c, h, w = inputs.shape
100 | ### 1/2
101 | conv1 = self.conv1(inputs)
102 | conv2 = self.conv2(conv1)
103 |
104 | ### 1/4
105 | conv3 = self.conv3(conv2)
106 | conv4 = self.conv4(conv3)
107 |
108 | ### 1/8
109 | conv5 = self.conv5(conv4)
110 | conv6 = self.conv6(conv5)
111 | ### 1/4
112 | conv7 = self.conv7(conv6)
113 | conv8 = self.conv8(torch.cat((conv7, conv4), dim=1))
114 | ### 1/2
115 | conv9 = self.conv9(conv8)
116 | conv10 = self.conv10(torch.cat((conv9, conv2), dim=1))
117 | ### output
118 | conv11 = self.conv11(conv10)
119 | conv12 = self.conv12(conv11)
120 | weights = torch.sigmoid(conv12)
121 |
122 | ws = torch.split(weights, 1, 1)
123 | hdr, weights = self.merge_HDR(ws, hdrs)
124 | return hdr
--------------------------------------------------------------------------------
/models/fusion_3E.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | from .network_utils import *
4 | class Downsample(nn.Module):
5 | def __init__(self, c_in, c_out=512, use_bn=True, afunc='LReLU'):
6 | super(Downsample, self).__init__()
7 |
8 | self.layers = torch.nn.Sequential(
9 | conv_layer(c_in, c_out//4, k=3, stride=2, pad=1, afunc=afunc, use_bn=use_bn),
10 | conv_layer(c_out//4, c_out//4, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn),
11 | conv_layer(c_out//4, c_out//2, k=3, stride=2, pad=1, afunc=afunc, use_bn=use_bn),
12 | conv_layer(c_out//2, c_out//2, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn),
13 | conv_layer(c_out//2, c_out, k=3, stride=2, pad=1, afunc=afunc, use_bn=use_bn),
14 | conv_layer(c_out, c_out, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn),
15 | )
16 |
17 | def forward(self, inputs):
18 | out = self.layers(inputs)
19 | return out
20 |
21 | class Upsample(nn.Module):
22 | def __init__(self, c_in, c_out, use_bn=True, afunc='LReLU'):
23 | super(Upsample, self).__init__()
24 | last_c = max(128, c_in // 8)
25 | self.layers = torch.nn.Sequential(
26 | deconv_layer(c_in, c_in//2, use_bn=use_bn, afunc=afunc),
27 | conv_layer(c_in//2, c_in//2, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn),
28 | deconv_layer(c_in//2, c_in//2, use_bn=use_bn, afunc=afunc),
29 | conv_layer(c_in//2, c_in//2, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn),
30 | deconv_layer(c_in//2, last_c, use_bn=use_bn, afunc=afunc),
31 | output_conv(last_c, c_out, k=3, stride=1, pad=1),
32 | )
33 |
34 | def forward(self, inputs):
35 | out = self.layers(inputs)
36 | out = torch.sigmoid(out)
37 | return out
38 |
39 | class Fusion_Net(nn.Module):
40 | def __init__(self, c_in=30, c_out=15, c_mid=256):
41 | super(Fusion_Net, self).__init__()
42 |
43 | self.conv1 = torch.nn.Sequential(
44 | nn.Conv2d(c_in, c_mid//4, kernel_size=3, stride=2, padding=1),
45 | nn.BatchNorm2d(c_mid//4),
46 | nn.LeakyReLU(0.1, inplace=True))
47 | self.conv2 = torch.nn.Sequential(
48 | nn.Conv2d(c_mid//4, c_mid//4, kernel_size=3, stride=1, padding=1),
49 | nn.BatchNorm2d(c_mid//4),
50 | nn.LeakyReLU(0.1, inplace=True))
51 | self.conv3 = torch.nn.Sequential(
52 | nn.Conv2d(c_mid//4, c_mid//2, kernel_size=3, stride=2, padding=1),
53 | nn.BatchNorm2d(c_mid//2),
54 | nn.LeakyReLU(0.1, inplace=True))
55 | self.conv4 = torch.nn.Sequential(
56 | nn.Conv2d(c_mid//2, c_mid//2, kernel_size=3, stride=1, padding=1),
57 | nn.BatchNorm2d(c_mid//2),
58 | nn.LeakyReLU(0.1, inplace=True))
59 |
60 | self.conv5 = torch.nn.Sequential(
61 | nn.Conv2d(c_mid//2, c_mid, kernel_size=3, stride=2, padding=1),
62 | nn.BatchNorm2d(c_mid),
63 | nn.LeakyReLU(0.1, inplace=True))
64 | self.conv6 = torch.nn.Sequential(
65 | nn.Conv2d(c_mid, c_mid, kernel_size=3, stride=1, padding=1),
66 | nn.BatchNorm2d(c_mid),
67 | nn.LeakyReLU(0.1, inplace=True))
68 |
69 | self.conv7 = torch.nn.Sequential(
70 | nn.ConvTranspose2d(c_mid, c_mid//2, kernel_size=4, stride=2, padding=1),
71 | nn.BatchNorm2d(c_mid//2),
72 | nn.LeakyReLU(0.1, inplace=True))
73 | self.conv8 = torch.nn.Sequential(
74 | nn.Conv2d(c_mid, c_mid//2, kernel_size=3, stride=1, padding=1),
75 | nn.BatchNorm2d(c_mid//2),
76 | nn.LeakyReLU(0.1, inplace=True))
77 | self.conv9 = torch.nn.Sequential(
78 | nn.ConvTranspose2d(c_mid//2, c_mid//4, kernel_size=4, stride=2, padding=1),
79 | nn.BatchNorm2d(c_mid//4),
80 | nn.LeakyReLU(0.1, inplace=True))
81 | self.conv10 = torch.nn.Sequential(
82 | nn.Conv2d(c_mid//2, c_mid//4, kernel_size=3, stride=1, padding=1),
83 | nn.BatchNorm2d(c_mid//4),
84 | nn.LeakyReLU(0.1, inplace=True))
85 |
86 | self.conv11 = torch.nn.Sequential(
87 | nn.ConvTranspose2d(c_mid//4, 64, kernel_size=4, stride=2, padding=1),
88 | nn.BatchNorm2d(64),
89 | nn.LeakyReLU(0.1, inplace=True))
90 |
91 | self.conv12 = torch.nn.Sequential(
92 | nn.Conv2d(64, c_out, kernel_size=3, stride=1, padding=1))
93 |
94 |
95 | self.merge_HDR = MergeHDRModule()
96 |
97 | def forward(self, inputs, hdrs):
98 | pred = OrderedDict()
99 | n, c, h, w = inputs.shape
100 | ### 1/2
101 | conv1 = self.conv1(inputs)
102 | conv2 = self.conv2(conv1)
103 |
104 | ### 1/4
105 | conv3 = self.conv3(conv2)
106 | conv4 = self.conv4(conv3)
107 |
108 | ### 1/8
109 | conv5 = self.conv5(conv4)
110 | conv6 = self.conv6(conv5)
111 | ### 1/4
112 | conv7 = self.conv7(conv6)
113 | conv8 = self.conv8(torch.cat((conv7, conv4), dim=1))
114 | ### 1/2
115 | conv9 = self.conv9(conv8)
116 | conv10 = self.conv10(torch.cat((conv9, conv2), dim=1))
117 | ### output
118 | conv11 = self.conv11(conv10)
119 | conv12 = self.conv12(conv11)
120 | weights = torch.sigmoid(conv12)
121 |
122 | ws = torch.split(weights, 1, 1)
123 | hdr, weights = self.merge_HDR(ws, hdrs)
124 | return hdr
--------------------------------------------------------------------------------
/models/loss.py:
--------------------------------------------------------------------------------
1 | import math
2 | import torch
3 | import torch.nn as nn
4 | import torchvision
5 | import numpy as np
6 | import torch.nn.functional as F
7 | from .network_utils import backward_warp
8 |
9 | def tonemap(hdr_img, mu=5000):
10 | return (torch.log(1 + mu * hdr_img)) / math.log(1 + mu)
11 |
12 | class HDRFlow_Loss_2E(nn.Module):
13 | def __init__(self, mu=5000):
14 | super(HDRFlow_Loss_2E, self).__init__()
15 | self.mu = mu
16 |
17 | def forward(self, pred, hdrs, flow_preds, cur_ldr, flow_mask, flow_gts):
18 | gt = hdrs[1]
19 | mu_pred = tonemap(pred, self.mu)
20 | mu_gt = tonemap(gt, self.mu)
21 | recon_loss = nn.L1Loss()(mu_pred, mu_gt)
22 | loss = recon_loss
23 |
24 | Y = 0.299 * cur_ldr[:, 0] + 0.587 * cur_ldr[:, 1] + 0.114 * cur_ldr[:, 2]
25 | Y = Y[:, None]
26 | mask = (Y>0.8) | (Y<0.2)
27 | mask = mask.repeat(1, 3, 1, 1)
28 | p_flow, n_flow = flow_preds
29 | if mask.sum() > 0:
30 | p_warp_hdr = backward_warp(hdrs[0], p_flow)
31 | n_warp_hdr = backward_warp(hdrs[2], n_flow)
32 |
33 | mu_p_warp_hdr = tonemap(p_warp_hdr, self.mu)
34 | mu_n_warp_hdr = tonemap(n_warp_hdr, self.mu)
35 |
36 | p_align_loss = nn.L1Loss()(mu_p_warp_hdr[mask], mu_gt[mask])
37 | n_align_loss = nn.L1Loss()(mu_n_warp_hdr[mask], mu_gt[mask])
38 |
39 | hdr_align_loss = p_align_loss + n_align_loss
40 | loss += 0.5 * hdr_align_loss
41 |
42 | b, c, h, w = p_flow.shape
43 | flow_mask = flow_mask[:, None, None, None].repeat(1, 2, h, w)
44 | flow_mask = flow_mask > 0.5
45 | if flow_mask.sum() > 0:
46 | p_flow_loss = nn.L1Loss()(p_flow[flow_mask], flow_gts[0][flow_mask])
47 | n_flow_loss = nn.L1Loss()(n_flow[flow_mask], flow_gts[1][flow_mask])
48 |
49 | flow_loss = p_flow_loss + n_flow_loss
50 | loss += 0.001 * flow_loss
51 |
52 | return loss
53 |
54 | class HDRFlow_Loss_3E(nn.Module):
55 | def __init__(self, mu=5000):
56 | super(HDRFlow_Loss_3E, self).__init__()
57 | self.mu = mu
58 |
59 | def forward(self, pred, hdrs, flow_preds, cur_ldr, flow_mask, flow_gts):
60 | gt = hdrs[2]
61 | mu_pred = tonemap(pred, self.mu)
62 | mu_gt = tonemap(gt, self.mu)
63 | recon_loss = nn.L1Loss()(mu_pred, mu_gt)
64 | loss = recon_loss
65 |
66 | Y = 0.299 * cur_ldr[:, 0] + 0.587 * cur_ldr[:, 1] + 0.114 * cur_ldr[:, 2]
67 | Y = Y[:, None]
68 | mask = (Y>0.8) | (Y<0.2)
69 | mask = mask.repeat(1, 3, 1, 1)
70 | p2_flow, n1_flow, p1_flow, n2_flow = flow_preds
71 | if mask.sum() > 0:
72 | p2_warp_hdr = backward_warp(hdrs[0], p2_flow)
73 | n1_warp_hdr = backward_warp(hdrs[3], n1_flow)
74 | p1_warp_hdr = backward_warp(hdrs[1], p1_flow)
75 | n2_warp_hdr = backward_warp(hdrs[4], n2_flow)
76 |
77 | mu_p2_warp_hdr = tonemap(p2_warp_hdr, self.mu)
78 | mu_n1_warp_hdr = tonemap(n1_warp_hdr, self.mu)
79 | mu_p1_warp_hdr = tonemap(p1_warp_hdr, self.mu)
80 | mu_n2_warp_hdr = tonemap(n2_warp_hdr, self.mu)
81 |
82 | p2_align_loss = nn.L1Loss()(mu_p2_warp_hdr[mask], mu_gt[mask])
83 | n1_align_loss = nn.L1Loss()(mu_n1_warp_hdr[mask], mu_gt[mask])
84 | p1_align_loss = nn.L1Loss()(mu_p1_warp_hdr[mask], mu_gt[mask])
85 | n2_align_loss = nn.L1Loss()(mu_n2_warp_hdr[mask], mu_gt[mask])
86 |
87 | hdr_align_loss = p2_align_loss + n1_align_loss + p1_align_loss + n2_align_loss
88 | loss += 0.2 * hdr_align_loss
89 |
90 | b, c, h, w = p2_flow.shape
91 | flow_mask = flow_mask[:, None, None, None].repeat(1, 2, h, w)
92 | flow_mask = flow_mask > 0.5
93 | if flow_mask.sum() > 0:
94 | p2_flow_loss = nn.L1Loss()(p2_flow[flow_mask], flow_gts[0][flow_mask])
95 | n1_flow_loss = nn.L1Loss()(n1_flow[flow_mask], flow_gts[1][flow_mask])
96 | p1_flow_loss = nn.L1Loss()(p1_flow[flow_mask], flow_gts[2][flow_mask])
97 | n2_flow_loss = nn.L1Loss()(n2_flow[flow_mask], flow_gts[3][flow_mask])
98 |
99 | flow_loss = p2_flow_loss + n1_flow_loss + p1_flow_loss + n2_flow_loss
100 | loss += 0.0005 * flow_loss
101 |
102 | return loss
103 |
--------------------------------------------------------------------------------
/models/model_2E.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | from .network_utils import *
4 | from .flow_2E import Flow_Net
5 | from .fusion_2E import Fusion_Net
6 |
7 | def cur_tone_perturb(cur, test_mode, d=0.7):
8 | if not test_mode:
9 | b, c, h, w = cur.shape
10 | gamma_aug = torch.exp(torch.rand(b, 3, 1, 1) * 2 * d - d)
11 | gamma_aug = gamma_aug.to(cur.device)
12 | cur_aug = torch.pow(cur, 1.0 / gamma_aug)
13 | else:
14 | cur_aug = cur
15 | return cur_aug
16 |
17 | def prepare_fusion_inputs(ldrs, pt_cur, expos, flows):
18 | prev, cur, nxt = ldrs
19 | p_exp, c_exp, n_exp = expos
20 | p_flow, n_flow = flows
21 | p_warp = backward_warp(prev, p_flow)
22 | n_warp = backward_warp(nxt, n_flow)
23 | p_warp_hdr = ldr_to_hdr(p_warp, p_exp)
24 | n_warp_hdr = ldr_to_hdr(n_warp, n_exp)
25 | c_hdr = ldr_to_hdr(cur, c_exp)
26 | p_hdr = ldr_to_hdr(prev, p_exp)
27 | n_hdr = ldr_to_hdr(nxt, n_exp)
28 | pt_c_hdr = ldr_to_hdr(pt_cur, c_exp)
29 | ldrs = [pt_cur, p_warp, n_warp, prev, nxt]
30 | hdrs = [pt_c_hdr, p_warp_hdr, n_warp_hdr, p_hdr, n_hdr]
31 | fusion_in = hdrs
32 | fusion_in += ldrs
33 | fusion_in = torch.cat(fusion_in, 1)
34 | fusion_hdrs = [c_hdr, p_warp_hdr, n_warp_hdr, p_hdr, n_hdr]
35 | return fusion_in, fusion_hdrs
36 |
37 | class HDRFlow(nn.Module):
38 | def __init__(self):
39 | super(HDRFlow, self).__init__()
40 | self.flow_net = Flow_Net()
41 | self.fusion_net = Fusion_Net(c_in=30, c_out=5, c_mid=128)
42 | def forward(self, ldrs, expos, test_mode=False):
43 | prev, cur, nxt = ldrs
44 | pt_cur = cur_tone_perturb(cur, test_mode)
45 | # flow net
46 | flow_preds = self.flow_net([prev, pt_cur, nxt], expos)
47 | fusion_in, fusion_hdrs = prepare_fusion_inputs(ldrs, pt_cur, expos, flow_preds)
48 | # fusion net
49 | pred_hdr = self.fusion_net(fusion_in, fusion_hdrs)
50 | return pred_hdr, flow_preds
51 |
--------------------------------------------------------------------------------
/models/model_3E.py:
--------------------------------------------------------------------------------
1 | import math
2 | import time
3 | import torch
4 | import torch.nn as nn
5 | from .network_utils import *
6 | from .flow_3E import Flow_Net
7 | from .fusion_3E import Fusion_Net
8 |
9 | def cur_tone_perturb(cur, test_mode, d=0.7):
10 | if not test_mode:
11 | b, c, h, w = cur.shape
12 | gamma_aug = torch.exp(torch.rand(b, 3, 1, 1) * 2 * d - d)
13 | gamma_aug = gamma_aug.to(cur.device)
14 | cur_aug = torch.pow(cur, 1.0 / gamma_aug)
15 | else:
16 | cur_aug = cur
17 | return cur_aug
18 |
19 | def prepare_fusion_inputs(ldrs, pt_c, expos, flow_preds):
20 | p2, p1, c, n1, n2 = ldrs
21 | p2_exp, p1_exp, c_exp, n1_exp, n2_exp = expos
22 | p2_flow, n1_flow, p1_flow, n2_flow = flow_preds
23 |
24 | p2_warp = backward_warp(p2, p2_flow)
25 | n1_warp = backward_warp(n1, n1_flow)
26 | p1_warp = backward_warp(p1, p1_flow)
27 | n2_warp = backward_warp(n2, n2_flow)
28 |
29 | p2_warp_hdr = ldr_to_hdr(p2_warp, p2_exp)
30 | n1_warp_hdr = ldr_to_hdr(n1_warp, n1_exp)
31 | p1_warp_hdr = ldr_to_hdr(p1_warp, p1_exp)
32 | n2_warp_hdr = ldr_to_hdr(n2_warp, n2_exp)
33 | c_hdr = ldr_to_hdr(c, c_exp)
34 | p2_hdr = ldr_to_hdr(p2, p2_exp)
35 | p1_hdr = ldr_to_hdr(p1, p1_exp)
36 | n1_hdr = ldr_to_hdr(n1, n1_exp)
37 | n2_hdr = ldr_to_hdr(n2, n2_exp)
38 | pt_c_hdr = ldr_to_hdr(pt_c, c_exp)
39 |
40 | fusion_in = [pt_c, pt_c_hdr, p2_warp, p2_warp_hdr, n1_warp, n1_warp_hdr, p1_warp, p1_warp_hdr, n2_warp, n2_warp_hdr, p2, p2_hdr, n1, n1_hdr, p1, p1_hdr, n2, n2_hdr]
41 | fusion_hdrs = [c_hdr, p2_warp_hdr, n1_warp_hdr, p1_warp_hdr, n2_warp_hdr, p2_hdr, n1_hdr, p1_hdr, n2_hdr]
42 | fusion_in = torch.cat(fusion_in, dim=1)
43 | return fusion_in, fusion_hdrs
44 |
45 | class HDRFlow(nn.Module):
46 | def __init__(self):
47 | super(HDRFlow, self).__init__()
48 | self.flow_net = Flow_Net()
49 | self.fusion_net = Fusion_Net(c_in=54, c_out=9, c_mid=256)
50 |
51 | def forward(self, ldrs, expos, test_mode=False):
52 | prev2, prev1, cur, nxt1, nxt2 = ldrs
53 | pt_cur = cur_tone_perturb(cur, test_mode)
54 | # flow net
55 | flow_preds = self.flow_net([prev2, prev1, pt_cur, nxt1, nxt2], expos)
56 | fusion_in, fusion_hdrs = prepare_fusion_inputs(ldrs, pt_cur, expos, flow_preds)
57 | # fusion net
58 | pred_hdr = self.fusion_net(fusion_in, fusion_hdrs)
59 | return pred_hdr, flow_preds
60 |
--------------------------------------------------------------------------------
/models/network_utils.py:
--------------------------------------------------------------------------------
1 | """
2 | Util functions for network construction
3 | """
4 | import os
5 | import importlib
6 | import torch
7 | import numpy as np
8 | import torch.nn as nn
9 | import torch.nn.functional as F
10 | from collections import OrderedDict
11 |
12 | def import_network(net_name, arch_dir='models.archs', backup_module='networks'):
13 | print('%s/%s' % (arch_dir.replace('.', '/'), net_name))
14 | if os.path.exists('%s/%s.py' % (arch_dir.replace('.', '/'), net_name)):
15 | network_file = importlib.import_module('%s.%s' % (arch_dir, net_name))
16 | else:
17 | network_file = importlib.import_module('%s.%s' % (arch_dir, backup_module))
18 | network = getattr(network_file, net_name)
19 | return network
20 |
21 | ## Common Network Blocks
22 | def activation(afunc='LReLU', inplace=True):
23 | if afunc == 'LReLU':
24 | return nn.LeakyReLU(0.1, inplace=inplace)
25 | elif afunc == 'LReLU02':
26 | return nn.LeakyReLU(0.2, inplace=inplace)
27 | elif afunc == 'ReLU':
28 | return nn.ReLU(inplace=inplace)
29 | elif afunc == 'Sigmoid':
30 | return nn.Sigmoid()
31 | elif afunc == 'Tanh':
32 | return nn.Tanh()
33 | else:
34 | raise Exception('Unknown activation function')
35 |
36 | def conv_layer(cin, cout, k=3, stride=1, pad=-1, dilation=1, afunc='LReLU', use_bn=False, bias=True, inplace=True):
37 | if type(pad) != tuple:
38 | pad = pad if pad >= 0 else (k - 1) // 2
39 | block = [nn.Conv2d(cin, cout, kernel_size=k, stride=stride, padding=pad, bias=bias, dilation=dilation)]
40 | if use_bn:
41 | # print('=> convolutional layer with bachnorm')
42 | block += [nn.BatchNorm2d(cout)]
43 | if afunc != '':
44 | block += [activation(afunc, inplace=inplace)]
45 | return nn.Sequential(*block)
46 |
47 | def deconv_layer(cin, cout, afunc='LReLU', use_bn=False, bias=False):
48 | block = [nn.ConvTranspose2d(cin, cout, kernel_size=4, stride=2, padding=1, bias=bias)]
49 | if use_bn:
50 | # print('=> deconvolutional layer with bachnorm')
51 | block += [nn.BatchNorm2d(cout)]
52 | block += [activation(afunc)]
53 | return nn.Sequential(*block)
54 |
55 | def upconv_layer(cin, cout, afunc='LReLU', mode='bilinear', use_bn=False, bias=True):
56 | if mode == 'bilinear':
57 | block = [nn.Upsample(scale_factor=2, mode=mode, align_corners=True)]
58 | elif mode == 'nearest':
59 | block = [nn.Upsample(scale_factor=2, mode=mode)]
60 | else:
61 | raise Exception('Unknonw mode: %s' % mode)
62 | block += [nn.Conv2d(cin, cout, kernel_size=3, stride=1, padding=1, bias=bias)]
63 | if use_bn:
64 | # print('=> deconvolutional layer with bachnorm')
65 | block += [nn.BatchNorm2d(cout)]
66 | block += [activation(afunc)]
67 | return nn.Sequential(*block)
68 |
69 | def up_nearest_layer(cin, cout, afunc='LReLU', use_bn=False, bias=True):
70 | block = [nn.Upsample(scale_factor=2, mode='nearest'),
71 | nn.Conv2d(cin, cout, kernel_size=3, stride=1, padding=1, bias=bias)]
72 | if use_bn:
73 | # print('=> deconvolutional layer with bachnorm')
74 | block += [nn.BatchNorm2d(cout)]
75 | block += [activation(afunc)]
76 | return nn.Sequential(*block)
77 |
78 | def output_conv(cin, cout, k=1, stride=1, pad=0, bias=True):
79 | pad = (k - 1) // 2
80 | return nn.Sequential(
81 | nn.Conv2d(cin, cout, kernel_size=k, stride=stride, padding=pad, bias=bias)
82 | )
83 |
84 | def fc_layer(cin, cout):
85 | return nn.Sequential(
86 | nn.Linear(cin, cout),
87 | nn.LeakyReLU(0.1, inplace=True))
88 |
89 | def down_block(cin, cout, k=3, layers=2, down='conv', afunc='LReLU', use_bn=True):
90 | block = []
91 | if down == 'conv':
92 | block += [conv_layer(cin, cout, k=3, stride=2, pad=1, afunc=afunc, use_bn=use_bn)]
93 | elif down == 'max':
94 | block += [conv_layer(cin, cout, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn)]
95 | block += [torch.nn.MaxPool2d(2)]
96 | elif down == 'avg':
97 | block += [conv_layer(cin, cout, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn)]
98 | block += [torch.nn.AvgPool2d(2)]
99 | else:
100 | raise Exception('Unknown downsample mode %s' % (down))
101 | for i in range(1, layers):
102 | block += [conv_layer(cout, cout, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn)]
103 | return nn.Sequential(*block)
104 |
105 | def up_block(cin, cout, layers=2, up='bilinear', afunc='LReLU', use_bn=True):
106 | block = []
107 | if up == 'deconv':
108 | block += [deconv_layer(cin, cout, afunc=afunc, use_bn=use_bn)]
109 | elif up == 'bilinear':
110 | block += [upconv_layer(cin, cout, afunc=afunc, mode='bilinear', use_bn=use_bn)]
111 | elif up == 'nearest':
112 | block += [upconv_layer(cin, cout, afunc=afunc, mode='nearest', use_bn=use_bn)]
113 | else:
114 | raise Exception('Unknown upsample mode: %s' % up)
115 | for i in range(1, layers):
116 | block += [conv_layer(cout, cout, k=3, stride=1, pad=1, afunc=afunc, use_bn=use_bn)]
117 | return nn.Sequential(*block)
118 |
119 | # For ResNet
120 | def make_layer(block, cin, channel, layer_num, use_bn=True): # TODO: clean
121 | layers = []
122 | for i in range(0, layer_num):
123 | layers.append(block(cin, channel, stride=1, downsample=None, use_bn=use_bn))
124 | return nn.Sequential(*layers)
125 |
126 | def conv3x3(in_planes, out_planes, stride=1):
127 | "3x3 convolution with padding"
128 | return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False)
129 |
130 | class BasicBlock(nn.Module):
131 | expansion = 1
132 | def __init__(self, inplanes, planes, stride=1, dilation=1, downsample=None, use_bn=True):
133 | super(BasicBlock, self).__init__()
134 | self.use_bn = use_bn
135 | self.conv1 = conv3x3(inplanes, planes, stride)
136 | if use_bn:
137 | self.bn1 = nn.BatchNorm2d(planes)
138 | self.relu = nn.ReLU(inplace=True)
139 | self.conv2 = conv3x3(planes, planes)
140 | if use_bn:
141 | self.bn2 = nn.BatchNorm2d(planes)
142 | self.downsample = downsample
143 | self.stride = stride
144 |
145 | def forward(self, x):
146 | residual = x
147 |
148 | out = self.conv1(x)
149 | if self.use_bn:
150 | out = self.bn1(out)
151 | out = self.relu(out)
152 |
153 | out = self.conv2(out)
154 | if self.use_bn:
155 | out = self.bn2(out)
156 | if self.downsample is not None:
157 | residual = self.downsample(x)
158 | out += residual
159 | out = self.relu(out)
160 | return out
161 |
162 | def merge_feats(ws, feats):
163 | assert(len(ws) == len(feats))
164 | w_sum = torch.stack(ws, 1).sum(1)
165 | ws = [w / (w_sum + 1e-8) for w in ws]
166 |
167 | feat = ws[0] * feats[0]
168 | for i in range(1, len(ws)):
169 | feat += ws[i] * feats[i]
170 | return feat, ws
171 |
172 | ## HDR output processing
173 | def merge_hdr(ws, hdrs):
174 | assert(len(ws) == len(hdrs))
175 | w_sum = torch.stack(ws, 1).sum(1)
176 | ws = [w / (w_sum + 1e-8) for w in ws]
177 |
178 | hdr = ws[0] * hdrs[0]
179 | for i in range(1, len(ws)):
180 | hdr += ws[i] * hdrs[i]
181 | return hdr, ws
182 |
183 | class MergeHDRModule(nn.Module):
184 | def __init__(self):
185 | super(MergeHDRModule, self).__init__()
186 |
187 | def forward(self, ws, hdrs):
188 | assert(len(ws) == len(hdrs))
189 | w_sum = torch.stack(ws, 1).sum(1)
190 | ws = [w / (w_sum + 1e-8) for w in ws]
191 |
192 | hdr = ws[0] * hdrs[0]
193 | for i in range(1, len(ws)):
194 | hdr += ws[i] * hdrs[i]
195 | return hdr, ws
196 |
197 | def coords_grid(b, h, w, device):
198 | coords = torch.meshgrid(torch.arange(h, device=device), torch.arange(w, device=device))
199 | coords = torch.stack(coords[::-1], dim=0).float()
200 | return coords[None].repeat(b, 1, 1, 1)
201 |
202 | def backward_warp(img, flow, pad='zeros'):
203 | b, c, h, w = img.shape
204 | grid = coords_grid(b, h, w, device=img.device)
205 | grid = grid + flow
206 | xgrid, ygrid = grid.split([1,1], dim=1)
207 | xgrid = 2*xgrid/(w-1) - 1
208 | ygrid = 2*ygrid/(h-1) - 1
209 | grid = torch.cat([xgrid, ygrid], dim=1)
210 | warped_img = F.grid_sample(input=img, grid=grid.permute(0,2,3,1), mode='bilinear', padding_mode='zeros')
211 | return warped_img
212 |
213 | def affine_warp(img, theta): # warp img1 to img2
214 | n, c, h, w = img.shape
215 | affine_grid = F.affine_grid(theta, img.shape)
216 | invalid_mask = ((affine_grid.narrow(3, 0, 1).abs() > 1) + (affine_grid.narrow(3, 1, 1).abs() > 1)) >= 1
217 | invalid_mask = invalid_mask.view(n, 1, h, w).float()
218 | img1_to_img2 = F.grid_sample(img, affine_grid)
219 | img1_to_img2 = img * invalid_mask + img1_to_img2 * (1 - invalid_mask)
220 |
221 | return img1_to_img2
222 |
223 | def ldr_to_hdr(ldr, expo, gamma=2.2):
224 | ldr = ldr.clamp(0, 1)
225 | ldr = torch.pow(ldr, gamma)
226 | expo = expo.view(-1, 1, 1, 1)
227 | hdr = ldr / expo
228 | return hdr
229 |
230 | def adj_expo_ldr_to_ldr(ldr, c_exp, p_exp, gamma=2.2):
231 | gain = torch.pow(p_exp / c_exp, 1.0 / gamma)
232 | gain = gain.view(-1, 1, 1, 1)
233 | ldr = (ldr * gain).clamp(0, 1)
234 | return ldr
235 |
236 | def resize_flow(flow, h, w):
237 | b, c, c_h, c_w = flow.shape
238 | flow = F.interpolate(input=flow, size=(h, w), mode='bilinear', align_corners=False)
239 | flow[:, 0] *= float(w) / float(c_w) # rescale flow magnitude after rescaling spatial size
240 | flow[:, 1] *= float(h) / float(c_h)
241 | return flow
242 |
243 | def resize_img_to_factor_of_k(img, k=64, mode='bilinear'):
244 | b, c, h, w = img.shape
245 | new_h = int(np.ceil(h / float(k)) * k)
246 | new_w = int(np.ceil(w / float(k)) * k)
247 | img = F.interpolate(input=img, size=(new_h, new_w), mode=mode, align_corners=False)
248 | return img
249 |
250 | def pad_img_to_factor_of_k(img, k=64, mode='replicate'):
251 | if img.ndimension() == 4:
252 | b, c, h, w = img.shape
253 | pad_h, pad_w = k - h % k, k - w % k
254 | img = F.pad(img, (0, pad_w, 0, pad_h), mode='replicate')
255 | elif img.ndimension() == 5:
256 | h, w = img.shape[-2], img.shape[-1]
257 | pad_h, pad_w = k - h % k, k - w % k
258 | img = F.pad(img, (0, pad_w, 0, pad_h, 0, 0), mode='replicate')
259 | return img
260 |
261 |
--------------------------------------------------------------------------------
/pretrained_models/2E/checkpoint.pth:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/pretrained_models/2E/checkpoint.pth
--------------------------------------------------------------------------------
/pretrained_models/3E/checkpoint.pth:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/pretrained_models/3E/checkpoint.pth
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | tqdm
2 | imageio
3 | opencv-python
4 | scikit-image
--------------------------------------------------------------------------------
/test_2E.py:
--------------------------------------------------------------------------------
1 | import os
2 | os.environ['CUDA_VISIBLE_DEVICES'] = '0'
3 | import os.path as osp
4 | import sys
5 | import time
6 | import argparse
7 | from tqdm import tqdm
8 | import glob
9 | import logging
10 | import torch
11 | import torch.nn as nn
12 | from torch.utils.data import DataLoader
13 | from dataset.real_benchmark_dataset import Real_Benchmark_Dataset
14 | from dataset.syn_test_dataset import Syn_Test_Dataset
15 | from models.model_2E import HDRFlow
16 | from utils.utils import *
17 | from skimage.metrics import structural_similarity as ssim
18 | from skimage.metrics import peak_signal_noise_ratio as psnr
19 | from utils import flow_viz
20 |
21 | parser = argparse.ArgumentParser(description="Test Setting")
22 | parser.add_argument("--dataset", type=str, default='DeepHDRVideo', choices=['DeepHDRVideo', 'CinematicVideo'],
23 | help='dataset directory')
24 | parser.add_argument("--dataset_dir", type=str, default='data/dynamic_RGB_data_2exp_release',
25 | help='dataset directory')
26 | # parser.add_argument("--dataset_dir", type=str, default='data/static_RGB_data_2exp_rand_motion_release',
27 | # help='dataset directory')
28 | # parser.add_argument("--dataset_dir", type=str, default='data/HDR_Synthetic_Test_Dataset',
29 | # help='dataset directory')
30 | parser.add_argument('--pretrained_model', type=str, default='./pretrained_models/2E/checkpoint.pth')
31 | parser.add_argument('--save_results', action='store_true', default=True)
32 | parser.add_argument('--save_dir', type=str, default="./output_results/2E")
33 |
34 | def save_flo(flow_preds, i, args):
35 | p_flo, n_flo = flow_preds
36 | p_flo = torch.squeeze(p_flo).permute(1,2,0).cpu().numpy()
37 | p_flo = flow_viz.flow_to_image(p_flo)
38 | n_flo = torch.squeeze(n_flo).permute(1,2,0).cpu().numpy()
39 | n_flo = flow_viz.flow_to_image(n_flo)
40 |
41 | concat_flo = np.concatenate([p_flo, n_flo], axis=1)
42 | dataset_name = args.dataset_dir.split('/')[-1]
43 | save_dir = os.path.join(args.save_dir, dataset_name, 'flow_preds')
44 | os.makedirs(save_dir, exist_ok=True)
45 | flo_path = os.path.join(save_dir, f'{i}_flow.png')
46 | cv2.imwrite(flo_path, concat_flo[:, :, [2,1,0]].astype('uint8'))
47 |
48 | def main():
49 | # Settings
50 | args = parser.parse_args()
51 | # pretrained_model
52 | print(">>>>>>>>> Start Testing >>>>>>>>>")
53 | print("Load weights from: ", args.pretrained_model)
54 | device = torch.device('cuda')
55 | model = HDRFlow()
56 | model.to(device)
57 | model = nn.DataParallel(model)
58 | model.load_state_dict(torch.load(args.pretrained_model)['state_dict'])
59 | model.eval()
60 |
61 | # test_loader = fetch_benchmarkloader(args)
62 | if args.dataset == 'DeepHDRVideo':
63 | test_dataset = Real_Benchmark_Dataset(root_dir=args.dataset_dir, nframes=3, nexps=2)
64 | elif args.dataset == 'CinematicVideo':
65 | test_dataset = Syn_Test_Dataset(root_dir=args.dataset_dir, nframes=3, nexps=2)
66 | else:
67 | print('Unknown dataset')
68 |
69 | test_loader = DataLoader(test_dataset, batch_size=1, shuffle=False, num_workers=8, pin_memory=True)
70 |
71 |
72 | psnrL_list = AverageMeter()
73 | ssimL_list = AverageMeter()
74 | psnrT_list = AverageMeter()
75 | ssimT_list = AverageMeter()
76 |
77 | low_psnrL_list = AverageMeter()
78 | low_psnrT_list = AverageMeter()
79 | high_psnrL_list = AverageMeter()
80 | high_psnrT_list = AverageMeter()
81 |
82 | with torch.no_grad():
83 | for idx, img_data in enumerate(test_loader):
84 | ldrs = [x.to(device) for x in img_data['ldrs']]
85 | expos = [x.to(device) for x in img_data['expos']]
86 | # hdrs = [x.to(device) for x in img_data['hdrs']]
87 | gt_hdr = img_data['hdr']
88 | padder = InputPadder(ldrs[0].shape, divis_by=16)
89 | pad_ldrs = padder.pad(ldrs)
90 | pred_hdr, flow_preds = model(pad_ldrs, expos, test_mode=True)
91 | pred_hdr = padder.unpad(pred_hdr)
92 | pred_hdr = torch.squeeze(pred_hdr.detach().cpu()).numpy().astype(np.float32).transpose(1,2,0)
93 |
94 | save_flo(flow_preds, idx+1, args)
95 | cur_ldr = torch.squeeze(ldrs[1].cpu()).numpy().astype(np.float32).transpose(1,2,0)
96 | Y = 0.299 * cur_ldr[:, :, 0] + 0.587 * cur_ldr[:, :, 1] + 0.114 * cur_ldr[:, :, 2]
97 | Y = Y[:, :, None]
98 | if expos[1] <= 1.:
99 | mask = Y < 0.2
100 | else:
101 | mask = Y > 0.8
102 | cur_linear_ldr = ldr_to_hdr(ldrs[1], expos[1])
103 | cur_linear_ldr = torch.squeeze(cur_linear_ldr.cpu()).numpy().astype(np.float32).transpose(1,2,0)
104 | pred_hdr = (~mask) * cur_linear_ldr + (mask) * pred_hdr
105 | gt_hdr = torch.squeeze(gt_hdr).numpy().astype(np.float32).transpose(1,2,0)
106 | gt_hdr_tm = tonemap(gt_hdr)
107 | pred_hdr_tm = tonemap(pred_hdr)
108 |
109 | psnrL = psnr(gt_hdr, pred_hdr)
110 | ssimL = ssim(gt_hdr, pred_hdr, multichannel=True, channel_axis=2, data_range=gt_hdr.max()-gt_hdr.min())
111 |
112 | psnrT = psnr(gt_hdr_tm, pred_hdr_tm)
113 | ssimT = ssim(gt_hdr_tm, pred_hdr_tm, multichannel=True, channel_axis=2, data_range=gt_hdr_tm.max()-gt_hdr_tm.min())
114 |
115 | psnrL_list.update(psnrL)
116 | ssimL_list.update(ssimL)
117 | psnrT_list.update(psnrT)
118 | ssimT_list.update(ssimT)
119 |
120 | if expos[1] <= 1.:
121 | print("Iter: [{}/{}] Low Exposure".format(idx+1, len(test_loader)))
122 | low_psnrL_list.update(psnrL)
123 | low_psnrT_list.update(psnrT)
124 | else:
125 | print("Iter: [{}/{}] High Exposure".format(idx+1, len(test_loader)))
126 | high_psnrL_list.update(psnrL)
127 | high_psnrT_list.update(psnrT)
128 |
129 | print("PSNR_mu: {:.4f} PSNR_l: {:.4f}".format(psnrT, psnrL))
130 | print("SSIM_mu: {:.4f} SSIM_l: {:.4f}".format(ssimT, ssimL))
131 |
132 | # save results
133 | if args.save_results:
134 | dataset_name = args.dataset_dir.split('/')[-1]
135 | hdr_output_dir = os.path.join(args.save_dir, dataset_name, 'hdr_output')
136 | if not osp.exists(hdr_output_dir):
137 | os.makedirs(hdr_output_dir)
138 | cv2.imwrite(os.path.join(hdr_output_dir, '{}_pred.png'.format(idx+1)), (pred_hdr_tm*255.)[:,:,[2,1,0]].astype('uint8'))
139 | # save_hdr(os.path.join(args.save_dir, img_data['hdr_path'][0]), pred_hdr)
140 |
141 | print("Low Average PSNRT: {:.4f} PSNRL: {:.4f}".format(low_psnrT_list.avg, low_psnrL_list.avg))
142 | print("High Average PSNRT: {:.4f} PSNRL: {:.4f}".format(high_psnrT_list.avg, high_psnrL_list.avg))
143 | print("Average PSNRT: {:.4f} PSNRL: {:.4f}".format(psnrT_list.avg, psnrL_list.avg))
144 | print("Average SSIMT: {:.4f} SSIML: {:.4f}".format(ssimT_list.avg, ssimL_list.avg))
145 | print(">>>>>>>>> Finish Testing >>>>>>>>>")
146 |
147 |
148 | if __name__ == '__main__':
149 | main()
150 |
--------------------------------------------------------------------------------
/test_3E.py:
--------------------------------------------------------------------------------
1 | import os
2 | import os.path as osp
3 | import sys
4 | os.environ['CUDA_VISIBLE_DEVICES'] = '0'
5 | import time
6 | import argparse
7 | from tqdm import tqdm
8 | import glob
9 | import logging
10 | import torch
11 | import torch.nn as nn
12 | from torch.utils.data import DataLoader
13 | from dataset.real_benchmark_dataset import Real_Benchmark_Dataset
14 | from dataset.syn_test_dataset import Syn_Test_Dataset
15 | from models.model_3E import HDRFlow
16 | from utils.utils import *
17 | from skimage.metrics import structural_similarity as ssim
18 | from skimage.metrics import peak_signal_noise_ratio as psnr
19 | from utils import flow_viz
20 |
21 | parser = argparse.ArgumentParser(description="Test Setting")
22 | parser.add_argument("--dataset", type=str, default='DeepHDRVideo', choices=['DeepHDRVideo', 'CinematicVideo'],
23 | help='dataset directory')
24 | parser.add_argument("--dataset_dir", type=str, default='./data/dynamic_RGB_data_3exp_release',
25 | help='dataset directory')
26 | # parser.add_argument("--dataset_dir", type=str, default='./data/static_RGB_data_3exp_rand_motion_release',
27 | # help='dataset directory')
28 | # parser.add_argument("--dataset_dir", type=str, default='./data/HDR_Synthetic_Test_Dataset',
29 | # help='dataset directory')
30 | parser.add_argument('--pretrained_model', type=str, default='./pretrained_models/3E/checkpoint.pth')
31 | parser.add_argument('--save_results', action='store_true', default=True)
32 | parser.add_argument('--save_dir', type=str, default="./output_results/3E")
33 |
34 | def save_flo(flow_preds, i, args):
35 | p2_flo, n1_flo, p1_flo, n2_flo = flow_preds
36 | p1_flo = torch.squeeze(p1_flo).permute(1,2,0).cpu().numpy()
37 | p1_flo = flow_viz.flow_to_image(p1_flo)
38 | n1_flo = torch.squeeze(n1_flo).permute(1,2,0).cpu().numpy()
39 | n1_flo = flow_viz.flow_to_image(n1_flo)
40 | p2_flo = torch.squeeze(p2_flo).permute(1,2,0).cpu().numpy()
41 | p2_flo = flow_viz.flow_to_image(p2_flo)
42 | n2_flo = torch.squeeze(n2_flo).permute(1,2,0).cpu().numpy()
43 | n2_flo = flow_viz.flow_to_image(n2_flo)
44 | concat_flo = np.concatenate([p2_flo, n1_flo, p1_flo, n2_flo], axis=1)
45 | dataset_name = args.dataset_dir.split('/')[-1]
46 | save_dir = os.path.join(args.save_dir, dataset_name, 'flow_preds')
47 | os.makedirs(save_dir, exist_ok=True)
48 | flo_path = os.path.join(save_dir, f'{i}_flow.png')
49 | cv2.imwrite(flo_path, concat_flo[:, :, [2,1,0]].astype('uint8'))
50 |
51 | def main():
52 | # Settings
53 | args = parser.parse_args()
54 | # pretrained_model
55 | print(">>>>>>>>> Start Testing >>>>>>>>>")
56 | print("Load weights from: ", args.pretrained_model)
57 | device = torch.device('cuda')
58 | model = HDRFlow()
59 | model.to(device)
60 | model = nn.DataParallel(model)
61 | model.load_state_dict(torch.load(args.pretrained_model)['state_dict'])
62 | model.eval()
63 |
64 | # test_loader = fetch_benchmarkloader(args)
65 | if args.dataset == 'DeepHDRVideo':
66 | test_dataset = Real_Benchmark_Dataset(root_dir=args.dataset_dir, nframes=5, nexps=3)
67 | elif args.dataset == 'CinematicVideo':
68 | test_dataset = Syn_Test_Dataset(root_dir=args.dataset_dir, nframes=5, nexps=3)
69 | # elif args.dataset == 'HDRVideo_TOG13':
70 | # test_dataset = TOG13_online_align_Dataset(root_dir=args.dataset_dir, nframes=3, nexps=2)
71 | else:
72 | print('Unknown dataset')
73 | test_loader = DataLoader(test_dataset, batch_size=1, shuffle=False, num_workers=8, pin_memory=True)
74 |
75 | psnr_l = AverageMeter()
76 | ssim_l = AverageMeter()
77 | psnr_mu = AverageMeter()
78 | ssim_mu = AverageMeter()
79 |
80 | low_psnr_l = AverageMeter()
81 | low_psnr_mu = AverageMeter()
82 | mid_psnr_l = AverageMeter()
83 | mid_psnr_mu = AverageMeter()
84 | high_psnr_l = AverageMeter()
85 | high_psnr_mu = AverageMeter()
86 |
87 | with torch.no_grad():
88 | for idx, img_data in enumerate(test_loader):
89 | ldrs = [x.to(device) for x in img_data['ldrs']]
90 | expos = [x.to(device) for x in img_data['expos']]
91 | # hdrs = [x.to(device) for x in img_data['hdrs']]
92 | gt_hdr = img_data['hdr']
93 |
94 | padder = InputPadder(ldrs[0].shape, divis_by=16)
95 | pad_ldrs = padder.pad(ldrs)
96 | pred_hdr, flow_preds = model(pad_ldrs, expos, test_mode=True)
97 | pred_hdr = padder.unpad(pred_hdr)
98 | pred_hdr = torch.squeeze(pred_hdr.detach().cpu()).numpy().astype(np.float32).transpose(1,2,0)
99 | save_flo(flow_preds, idx+1, args)
100 | cur_ldr = torch.squeeze(ldrs[2].cpu()).numpy().astype(np.float32).transpose(1,2,0)
101 | Y = 0.299 * cur_ldr[:, :, 0] + 0.587 * cur_ldr[:, :, 1] + 0.114 * cur_ldr[:, :, 2]
102 | Y = Y[:, :, None]
103 |
104 | if expos[2] == 1.:
105 | mask = Y < 0.2
106 | elif expos[2] == 4.:
107 | mask = (Y < 0.2) | (Y > 0.8)
108 | else:
109 | mask = Y > 0.8
110 | cur_linear_ldr = ldr_to_hdr(ldrs[2], expos[2])
111 | cur_linear_ldr = torch.squeeze(cur_linear_ldr.cpu()).numpy().astype(np.float32).transpose(1,2,0)
112 | pred_hdr = (~mask) * cur_linear_ldr + (mask) * pred_hdr
113 | gt_hdr = torch.squeeze(gt_hdr).numpy().astype(np.float32).transpose(1,2,0)
114 | pred_hdr = pred_hdr.copy()
115 | psnrL = psnr(gt_hdr, pred_hdr)
116 |
117 | gt_hdr_tm = tonemap(gt_hdr)
118 | pred_hdr_tm = tonemap(pred_hdr)
119 | psnrT = psnr(gt_hdr_tm, pred_hdr_tm)
120 |
121 | ssimL = ssim(gt_hdr, pred_hdr, multichannel=True, channel_axis=2, data_range=gt_hdr.max()-gt_hdr.min())
122 | ssimT = ssim(gt_hdr_tm, pred_hdr_tm, multichannel=True, channel_axis=2, data_range=gt_hdr_tm.max()-gt_hdr_tm.min())
123 |
124 | psnr_l.update(psnrL)
125 | ssim_l.update(ssimL)
126 | psnr_mu.update(psnrT)
127 | ssim_mu.update(ssimT)
128 |
129 | if expos[2] == 1.:
130 | print("Iter: [{}/{}] Low Exposure".format(idx+1, len(test_loader)))
131 | low_psnr_l.update(psnrL)
132 | low_psnr_mu.update(psnrT)
133 |
134 | elif expos[2] == 4.:
135 | print("Iter: [{}/{}] Mid Exposure".format(idx+1, len(test_loader)))
136 | mid_psnr_l.update(psnrL)
137 | mid_psnr_mu.update(psnrT)
138 |
139 | else:
140 | print("Iter: [{}/{}] High Exposure".format(idx+1, len(test_loader)))
141 | high_psnr_l.update(psnrL)
142 | high_psnr_mu.update(psnrT)
143 |
144 | print("PSNR_mu: {:.4f} PSNR_l: {:.4f}".format(psnrT, psnrL))
145 | print("SSIM_mu: {:.4f} SSIM_l: {:.4f}".format(ssimT, ssimL))
146 |
147 | # save results
148 | if args.save_results:
149 | dataset_name = args.dataset_dir.split('/')[-1]
150 | hdr_output_dir = os.path.join(args.save_dir, dataset_name, 'hdr_output')
151 | if not osp.exists(hdr_output_dir):
152 | os.makedirs(hdr_output_dir)
153 | cv2.imwrite(os.path.join(hdr_output_dir, '{}_pred.png'.format(idx+1)), (pred_hdr_tm*255.)[:,:,[2,1,0]].astype('uint8'))
154 | # save_hdr(os.path.join(args.save_dir, img_data['hdr_path'][0]), pred_hdr)
155 | print("Low Average PSNR_mu: {:.4f} PSNR_l: {:.4f}".format(low_psnr_mu.avg, low_psnr_l.avg))
156 | print("Mid Average PSNR_mu: {:.4f} PSNR_l: {:.4f}".format(mid_psnr_mu.avg, mid_psnr_l.avg))
157 | print("High Average PSNR_mu: {:.4f} PSNR_l: {:.4f}".format(high_psnr_mu.avg, high_psnr_l.avg))
158 | print("Average PSNR_mu: {:.4f} PSNR_l: {:.4f}".format(psnr_mu.avg, psnr_l.avg))
159 | print("Average SSIM_mu: {:.4f} SSIM_l: {:.4f}".format(ssim_mu.avg, ssim_l.avg))
160 | print(">>>>>>>>> Finish Testing >>>>>>>>>")
161 |
162 |
163 | if __name__ == '__main__':
164 | main()
165 |
166 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/test_tog13_2E.py:
--------------------------------------------------------------------------------
1 | import os
2 | import os.path as osp
3 | import sys
4 | os.environ['CUDA_VISIBLE_DEVICES'] = '0'
5 | import time
6 | import argparse
7 | from tqdm import tqdm
8 | import glob
9 | import logging
10 | import torch
11 | import torch.nn as nn
12 | from torch.utils.data import DataLoader
13 | from dataset.tog13_online_align_dataset import TOG13_online_align_Dataset
14 | from models.model_2E import HDRFlow
15 | from utils.utils import *
16 | from utils import flow_viz
17 |
18 | parser = argparse.ArgumentParser(description="Test Setting")
19 | parser.add_argument("--dataset_dir", type=str, default='data/TOG13_Dynamic_Dataset/ThrowingTowel-2Exp-3Stop',
20 | help='dataset directory')
21 | parser.add_argument('--pretrained_model', type=str, default='./pretrained_models/2E/checkpoint.pth')
22 | parser.add_argument('--save_results', action='store_true', default=True)
23 | parser.add_argument('--save_dir', type=str, default="output_results/2E/")
24 |
25 | def save_flo(flow_preds, i, args):
26 | p_flo, n_flo = flow_preds
27 | p_flo = torch.squeeze(p_flo).permute(1,2,0).cpu().numpy()
28 | p_flo = flow_viz.flow_to_image(p_flo)
29 | n_flo = torch.squeeze(n_flo).permute(1,2,0).cpu().numpy()
30 | n_flo = flow_viz.flow_to_image(n_flo)
31 |
32 | concat_flo = np.concatenate([p_flo, n_flo], axis=1)
33 | dataset_name = args.dataset_dir.split('/')[-1]
34 | save_dir = os.path.join(args.save_dir, dataset_name, 'flow_preds')
35 | os.makedirs(save_dir, exist_ok=True)
36 | flo_path = os.path.join(save_dir, f'{i}_flow.png')
37 | cv2.imwrite(flo_path, concat_flo[:, :, [2,1,0]].astype('uint8'))
38 |
39 | def main():
40 | # Settings
41 | args = parser.parse_args()
42 | # pretrained_model
43 | print(">>>>>>>>> Start Testing >>>>>>>>>")
44 | print("Load weights from: ", args.pretrained_model)
45 | device = torch.device('cuda')
46 | model = HDRFlow()
47 | model.to(device)
48 | model = nn.DataParallel(model)
49 | model.load_state_dict(torch.load(args.pretrained_model)['state_dict'])
50 | model.eval()
51 |
52 | test_dataset = TOG13_online_align_Dataset(root_dir=args.dataset_dir, nframes=3, nexps=2)
53 | test_loader = DataLoader(test_dataset, batch_size=1, shuffle=False, num_workers=8, pin_memory=True)
54 | with torch.no_grad():
55 | for idx, img_data in enumerate(test_loader):
56 | ldrs = [x.to(device) for x in img_data['ldrs']]
57 | expos = [x.to(device) for x in img_data['expos']]
58 | matches = [x.to(device) for x in img_data['matches']]
59 | align_ldrs = global_align_nbr_ldrs(ldrs, matches)
60 | padder = InputPadder(align_ldrs[0].shape, divis_by=16)
61 | pad_ldrs = padder.pad(align_ldrs)
62 | pred_hdr, flow_preds = model(pad_ldrs, expos, test_mode=True)
63 | pred_hdr = padder.unpad(pred_hdr)
64 | pred_hdr = torch.squeeze(pred_hdr.detach().cpu()).numpy().astype(np.float32).transpose(1,2,0)
65 | save_flo(flow_preds, idx+1, args)
66 |
67 | cur_ldr = torch.squeeze(ldrs[1].cpu()).numpy().astype(np.float32).transpose(1,2,0)
68 | Y = 0.299 * cur_ldr[:, :, 0] + 0.587 * cur_ldr[:, :, 1] + 0.114 * cur_ldr[:, :, 2]
69 | Y = Y[:, :, None]
70 |
71 | if expos[1] <= 1.:
72 | mask = Y < 0.2
73 | else:
74 | mask = Y > 0.8
75 |
76 | cur_linear_ldr = ldr_to_hdr(ldrs[1], expos[1])
77 | cur_linear_ldr = torch.squeeze(cur_linear_ldr.cpu()).numpy().astype(np.float32).transpose(1,2,0)
78 | pred_hdr = (~mask) * cur_linear_ldr + (mask) * pred_hdr
79 | pred_hdr_tm = tonemap(pred_hdr)
80 |
81 | # save results
82 | if args.save_results:
83 | dataset_name = args.dataset_dir.split('/')[-1]
84 | hdr_output_dir = os.path.join(args.save_dir, dataset_name, 'hdr_output')
85 | if not osp.exists(hdr_output_dir):
86 | os.makedirs(hdr_output_dir)
87 | save_hdr(os.path.join(hdr_output_dir, '{}_pred.hdr'.format(idx+1)), pred_hdr)
88 |
89 | if __name__ == '__main__':
90 | main()
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/test_tog13_3E.py:
--------------------------------------------------------------------------------
1 | import os
2 | import os.path as osp
3 | import sys
4 | os.environ['CUDA_VISIBLE_DEVICES'] = '0'
5 | import time
6 | import argparse
7 | from tqdm import tqdm
8 | import glob
9 | import logging
10 | import torch
11 | import torch.nn as nn
12 | from torch.utils.data import DataLoader
13 | from dataset.tog13_online_align_dataset import TOG13_online_align_Dataset
14 | from models.model_3E import HDRFlow
15 | from utils.utils import *
16 | from utils import flow_viz
17 |
18 | parser = argparse.ArgumentParser(description="Test Setting")
19 | parser.add_argument("--dataset_dir", type=str, default='data/TOG13_Dynamic_Dataset/Dog-3Exp-2Stop',
20 | help='dataset directory')
21 | parser.add_argument('--pretrained_model', type=str, default='./pretrained_models/3E/checkpoint.pth')
22 | parser.add_argument('--save_results', action='store_true', default=True)
23 | parser.add_argument('--save_dir', type=str, default="output_results/3E/")
24 |
25 | def save_flo(flow_preds, i, args):
26 | p2_flo, n1_flo, p1_flo, n2_flo = flow_preds
27 | p1_flo = torch.squeeze(p1_flo).permute(1,2,0).cpu().numpy()
28 | p1_flo = flow_viz.flow_to_image(p1_flo)
29 | n1_flo = torch.squeeze(n1_flo).permute(1,2,0).cpu().numpy()
30 | n1_flo = flow_viz.flow_to_image(n1_flo)
31 | p2_flo = torch.squeeze(p2_flo).permute(1,2,0).cpu().numpy()
32 | p2_flo = flow_viz.flow_to_image(p2_flo)
33 | n2_flo = torch.squeeze(n2_flo).permute(1,2,0).cpu().numpy()
34 | n2_flo = flow_viz.flow_to_image(n2_flo)
35 | concat_flo = np.concatenate([p2_flo, n1_flo, p1_flo, n2_flo], axis=1)
36 | dataset_name = args.dataset_dir.split('/')[-1]
37 | save_dir = os.path.join(args.save_dir, dataset_name, 'flow_preds')
38 | os.makedirs(save_dir, exist_ok=True)
39 | flo_path = os.path.join(save_dir, f'{i}_flow.png')
40 | cv2.imwrite(flo_path, concat_flo[:, :, [2,1,0]].astype('uint8'))
41 |
42 | def main():
43 | # Settings
44 | args = parser.parse_args()
45 | # pretrained_model
46 | print(">>>>>>>>> Start Testing >>>>>>>>>")
47 | print("Load weights from: ", args.pretrained_model)
48 | device = torch.device('cuda')
49 | model = HDRFlow()
50 | model.to(device)
51 | model = nn.DataParallel(model)
52 | model.load_state_dict(torch.load(args.pretrained_model)['state_dict'])
53 | model.eval()
54 | test_dataset = TOG13_online_align_Dataset(root_dir=args.dataset_dir, nframes=5, nexps=3)
55 | test_loader = DataLoader(test_dataset, batch_size=1, shuffle=False, num_workers=8, pin_memory=True)
56 | with torch.no_grad():
57 | for idx, img_data in enumerate(test_loader):
58 | ldrs = [x.to(device) for x in img_data['ldrs']]
59 | expos = [x.to(device) for x in img_data['expos']]
60 | matches = [x.to(device) for x in img_data['matches']]
61 | # hdrs = [x.to(device) for x in img_data['hdrs']]
62 | align_ldrs = global_align_nbr_ldrs(ldrs, matches)
63 | padder = InputPadder(align_ldrs[0].shape, divis_by=16)
64 | pad_ldrs = padder.pad(align_ldrs)
65 | pred_hdr, flow_preds = model(pad_ldrs, expos, test_mode=True)
66 | pred_hdr = padder.unpad(pred_hdr)
67 | pred_hdr = torch.squeeze(pred_hdr.detach().cpu()).numpy().astype(np.float32).transpose(1,2,0)
68 | save_flo(flow_preds, idx+1, args)
69 | cur_ldr = torch.squeeze(ldrs[2].cpu()).numpy().astype(np.float32).transpose(1,2,0)
70 | Y = 0.299 * cur_ldr[:, :, 0] + 0.587 * cur_ldr[:, :, 1] + 0.114 * cur_ldr[:, :, 2]
71 | Y = Y[:, :, None]
72 | if expos[2] == 1.:
73 | mask = Y < 0.2
74 | elif expos[2] == 4.:
75 | mask = (Y<0.2) | (Y>0.8)
76 | else:
77 | mask = Y > 0.8
78 |
79 | cur_linear_ldr = ldr_to_hdr(ldrs[2], expos[2])
80 | cur_linear_ldr = torch.squeeze(cur_linear_ldr.cpu()).numpy().astype(np.float32).transpose(1,2,0)
81 | pred_hdr = (~mask) * cur_linear_ldr + (mask) * pred_hdr
82 | # save results
83 | if args.save_results:
84 | dataset_name = args.dataset_dir.split('/')[-1]
85 | hdr_output_dir = os.path.join(args.save_dir, dataset_name, 'hdr_output')
86 | if not osp.exists(hdr_output_dir):
87 | os.makedirs(hdr_output_dir)
88 | save_hdr(os.path.join(hdr_output_dir, '{}_pred.hdr'.format(idx+1)), pred_hdr)
89 |
90 | if __name__ == '__main__':
91 | main()
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/train_2E.py:
--------------------------------------------------------------------------------
1 | import os
2 | os.environ['CUDA_VISIBLE_DEVICES'] = '0'
3 | import time
4 | import argparse
5 | from tqdm import tqdm
6 | import torch
7 | import torch.nn as nn
8 | from torch.utils.data import DataLoader
9 | from dataset import fetch_dataloader_2E
10 | from models.loss import HDRFlow_Loss_2E
11 | from models.model_2E import HDRFlow
12 | from utils.utils import *
13 |
14 | def get_args():
15 | parser = argparse.ArgumentParser(description='HDRFlow',
16 | formatter_class=argparse.ArgumentDefaultsHelpFormatter)
17 | parser.add_argument("--dataset_vimeo_dir", type=str, default='data/vimeo_septuplet',
18 | help='dataset directory'),
19 | parser.add_argument("--dataset_sintel_dir", type=str, default='data/Sintel/training/',
20 | help='dataset directory'),
21 | parser.add_argument('--logdir', type=str, default='./checkpoints_2E',
22 | help='target log directory')
23 | parser.add_argument('--num_workers', type=int, default=8, metavar='N',
24 | help='number of workers to fetch data (default: 8)')
25 | parser.add_argument('--resume', type=str, default=None,
26 | help='load model from a .pth file')
27 | parser.add_argument('--seed', type=int, default=443, metavar='S',
28 | help='random seed (default: 443)')
29 | parser.add_argument('--init_weights', action='store_true', default=False,
30 | help='init model weights')
31 | parser.add_argument('--lr', type=float, default=0.0001, metavar='LR',
32 | help='learning rate (default: 0.0002)')
33 | parser.add_argument('--lr_decay_epochs', type=str,
34 | default="20,30:2", help='the epochs to decay lr: the downscale rate')
35 | parser.add_argument('--start_epoch', type=int, default=0, metavar='N',
36 | help='start epoch of training (default: 1)')
37 | parser.add_argument('--epochs', type=int, default=40, metavar='N',
38 | help='number of epochs to train (default: 100)')
39 | parser.add_argument('--batch_size', type=int, default=16, metavar='N',
40 | help='training batch size (default: 16)')
41 | parser.add_argument('--val_batch_size', type=int, default=8, metavar='N',
42 | help='testing batch size (default: 1)')
43 | parser.add_argument('--log_interval', type=int, default=100, metavar='N',
44 | help='how many batches to wait before logging training status')
45 | return parser.parse_args()
46 |
47 |
48 | def train(args, model, device, train_loader, optimizer, epoch, hdrflow_loss):
49 | model.train()
50 | batch_time = AverageMeter()
51 | data_time = AverageMeter()
52 | end = time.time()
53 | for batch_idx, batch_data in enumerate(train_loader):
54 | data_time.update(time.time() - end)
55 | ldrs = [x.to(device) for x in batch_data['ldrs']]
56 | expos = [x.to(device) for x in batch_data['expos']]
57 | hdrs = [x.to(device) for x in batch_data['hdrs']]
58 | flow_gts = [x.to(device) for x in batch_data['flow_gts']]
59 | flow_mask = batch_data['flow_mask'].to(device)
60 | pred_hdr, flow_preds = model(ldrs, expos)
61 | cur_ldr = ldrs[1]
62 | loss = hdrflow_loss(pred_hdr, hdrs, flow_preds, cur_ldr, flow_mask, flow_gts)
63 | optimizer.zero_grad()
64 | loss.backward()
65 | optimizer.step()
66 | batch_time.update(time.time() - end)
67 | end = time.time()
68 | if batch_idx % args.log_interval == 0:
69 | print('Train Epoch: {} [{}/{} ({:.0f} %)]\tLoss: {:.6f}\t'
70 | 'Time: {batch_time.val:.3f} ({batch_time.avg:3f})\t'
71 | 'Data: {data_time.val:.3f} ({data_time.avg:3f})'.format(
72 | epoch,
73 | batch_idx,
74 | len(train_loader),
75 | 100. * batch_idx / len(train_loader),
76 | loss.item(),
77 | batch_time=batch_time,
78 | data_time=data_time
79 | ))
80 |
81 | def validation(args, model, device, val_loader, optimizer, epoch):
82 | model.eval()
83 | n_val = len(val_loader)
84 | val_psnr = AverageMeter()
85 | val_mu_psnr = AverageMeter()
86 | with torch.no_grad():
87 | for batch_idx, batch_data in enumerate(val_loader):
88 | ldrs = [x.to(device) for x in batch_data['ldrs']]
89 | expos = [x.to(device) for x in batch_data['expos']]
90 | hdrs = [x.to(device) for x in batch_data['hdrs']]
91 | gt_hdr = hdrs[1]
92 | pred_hdr, _ = model(ldrs, expos)
93 | psnr = batch_psnr(pred_hdr, gt_hdr, 1.0)
94 | mu_psnr = batch_psnr_mu(pred_hdr, gt_hdr, 1.0)
95 | val_psnr.update(psnr.item())
96 | val_mu_psnr.update(mu_psnr.item())
97 |
98 | print('Validation set: Number: {}'.format(n_val))
99 | print('Validation set: Average PSNR-l: {:.4f}, PSNR-mu: {:.4f}'.format(val_psnr.avg, val_mu_psnr.avg))
100 | save_dict = {
101 | 'epoch': epoch + 1,
102 | 'state_dict': model.state_dict(),
103 | 'optimizer': optimizer.state_dict()}
104 | torch.save(save_dict, os.path.join(args.logdir, 'checkpoint_%s.pth' % (epoch+1)))
105 | with open(os.path.join(args.logdir, 'checkpoint.json'), 'a') as f:
106 | f.write('epoch:' + str(epoch) + '\n')
107 | f.write('Validation set: Average PSNR-l: {:.4f}, PSNR-mu: {:.4f}\n'.format(val_psnr.avg, val_mu_psnr.avg))
108 |
109 |
110 | def main():
111 | args = get_args()
112 | if args.seed is not None:
113 | set_random_seed(args.seed)
114 | if not os.path.exists(args.logdir):
115 | os.makedirs(args.logdir)
116 | device = torch.device('cuda')
117 | # model
118 | model = HDRFlow()
119 | if args.init_weights:
120 | init_parameters(model)
121 | hdrflow_loss = HDRFlow_Loss_2E().to(device)
122 | # optimizer
123 | optimizer = torch.optim.Adam(model.parameters(), lr=args.lr, betas=(0.9, 0.999), eps=1e-08)
124 | model.to(device)
125 | model = nn.DataParallel(model)
126 |
127 | if args.resume:
128 | if os.path.isfile(args.resume):
129 | print("===> Loading checkpoint from: {}".format(args.resume))
130 | checkpoint = torch.load(args.resume)
131 | args.start_epoch = checkpoint['epoch']
132 | model.load_state_dict(checkpoint['state_dict'])
133 | optimizer.load_state_dict(checkpoint['optimizer'])
134 | print("===> Loaded checkpoint: epoch {}".format(checkpoint['epoch']))
135 | else:
136 | print("===> No checkpoint is founded at {}.".format(args.resume))
137 |
138 | train_loader, val_loader = fetch_dataloader_2E(args)
139 |
140 | for epoch in range(args.start_epoch, args.epochs):
141 | adjust_learning_rate(args, optimizer, epoch)
142 | train(args, model, device, train_loader, optimizer, epoch, hdrflow_loss)
143 | validation(args, model, device, val_loader, optimizer, epoch)
144 |
145 | if __name__ == '__main__':
146 | main()
147 |
--------------------------------------------------------------------------------
/train_3E.py:
--------------------------------------------------------------------------------
1 | import os
2 | os.environ['CUDA_VISIBLE_DEVICES'] = '0'
3 | import time
4 | import argparse
5 | from tqdm import tqdm
6 | import torch
7 | import torch.nn as nn
8 | from torch.utils.data import DataLoader
9 | from dataset import fetch_dataloader
10 | from models.loss import HDRFlow_Loss_3E
11 | from models.model_3E import HDRFlow
12 | from utils.utils import *
13 |
14 | def get_args():
15 | parser = argparse.ArgumentParser(description='HDRFlow',
16 | formatter_class=argparse.ArgumentDefaultsHelpFormatter)
17 | parser.add_argument("--dataset_vimeo_dir", type=str, default='data/vimeo_septuplet',
18 | help='dataset directory'),
19 | parser.add_argument("--dataset_sintel_dir", type=str, default='data/Sintel/training/',
20 | help='dataset directory'),
21 | parser.add_argument('--logdir', type=str, default='./checkpoints_3E',
22 | help='target log directory')
23 | parser.add_argument('--num_workers', type=int, default=8, metavar='N',
24 | help='number of workers to fetch data (default: 8)')
25 | # Training
26 | parser.add_argument('--resume', type=str, default=None,
27 | help='load model from a .pth file')
28 | parser.add_argument('--seed', type=int, default=443, metavar='S',
29 | help='random seed (default: 443)')
30 | parser.add_argument('--init_weights', action='store_true', default=False,
31 | help='init model weights')
32 | parser.add_argument('--lr', type=float, default=0.0001, metavar='LR',
33 | help='learning rate (default: 0.0002)')
34 | parser.add_argument('--lr_decay_epochs', type=str,
35 | default="20,30:2", help='the epochs to decay lr: the downscale rate')
36 | parser.add_argument('--start_epoch', type=int, default=1, metavar='N',
37 | help='start epoch of training (default: 1)')
38 | parser.add_argument('--epochs', type=int, default=40, metavar='N',
39 | help='number of epochs to train (default: 100)')
40 | parser.add_argument('--batch_size', type=int, default=16, metavar='N',
41 | help='training batch size (default: 16)')
42 | parser.add_argument('--val_batch_size', type=int, default=8, metavar='N',
43 | help='testing batch size (default: 1)')
44 | parser.add_argument('--log_interval', type=int, default=100, metavar='N',
45 | help='how many batches to wait before logging training status')
46 | return parser.parse_args()
47 |
48 |
49 | def train(args, model, device, train_loader, optimizer, epoch, hdrflow_loss):
50 | model.train()
51 | batch_time = AverageMeter()
52 | data_time = AverageMeter()
53 | end = time.time()
54 | for batch_idx, batch_data in enumerate(train_loader):
55 | data_time.update(time.time() - end)
56 | ldrs = [x.to(device) for x in batch_data['ldrs']]
57 | expos = [x.to(device) for x in batch_data['expos']]
58 | hdrs = [x.to(device) for x in batch_data['hdrs']]
59 | flow_gts = [x.to(device) for x in batch_data['flow_gts']]
60 | flow_mask = batch_data['flow_mask'].to(device)
61 | pred_hdr, flow_preds = model(ldrs, expos)
62 | cur_ldr = ldrs[2]
63 | loss = hdrflow_loss(pred, hdrs, flow_preds, cur_ldr, flow_mask, flow_gts)
64 | optimizer.zero_grad()
65 | loss.backward()
66 | optimizer.step()
67 | batch_time.update(time.time() - end)
68 | end = time.time()
69 | if batch_idx % args.log_interval == 0:
70 | print('Train Epoch: {} [{}/{} ({:.0f} %)]\tLoss: {:.6f}\t'
71 | 'Time: {batch_time.val:.3f} ({batch_time.avg:3f})\t'
72 | 'Data: {data_time.val:.3f} ({data_time.avg:3f})'.format(
73 | epoch,
74 | batch_idx,
75 | len(train_loader),
76 | 100. * batch_idx / len(train_loader),
77 | loss.item(),
78 | batch_time=batch_time,
79 | data_time=data_time
80 | ))
81 |
82 | def validation(args, model, device, val_loader, optimizer, epoch):
83 | model.eval()
84 | n_val = len(val_loader)
85 | val_psnr = AverageMeter()
86 | val_mu_psnr = AverageMeter()
87 | with torch.no_grad():
88 | for batch_idx, batch_data in enumerate(val_loader):
89 | ldrs = [x.to(device) for x in batch_data['ldrs']]
90 | expos = [x.to(device) for x in batch_data['expos']]
91 | hdrs = [x.to(device) for x in batch_data['hdrs']]
92 | gt_hdr = hdrs[2]
93 | pred_hdr, _ = model(ldrs, expos)
94 | psnr = batch_psnr(pred_hdr, gt_hdr, 1.0)
95 | mu_psnr = batch_psnr_mu(pred_hdr, gt_hdr, 1.0)
96 | val_psnr.update(psnr.item())
97 | val_mu_psnr.update(mu_psnr.item())
98 |
99 | print('Validation set: Number: {}'.format(n_val))
100 | print('Validation set: Average PSNR-l: {:.4f}, PSNR-mu: {:.4f}'.format(val_psnr.avg, val_mu_psnr.avg))
101 |
102 | save_dict = {
103 | 'epoch': epoch + 1,
104 | 'state_dict': model.state_dict(),
105 | 'optimizer': optimizer.state_dict()}
106 | torch.save(save_dict, os.path.join(args.logdir, 'checkpoint_%s.pth' % (epoch+1)))
107 |
108 | with open(os.path.join(args.logdir, 'checkpoint.json'), 'a') as f:
109 | f.write('epoch:' + str(epoch) + '\n')
110 | f.write('Validation set: Average PSNR-l: {:.4f}, PSNR-mu: {:.4f}\n'.format(val_psnr.avg, val_mu_psnr.avg))
111 |
112 | def main():
113 | args = get_args()
114 | if args.seed is not None:
115 | set_random_seed(args.seed)
116 | if not os.path.exists(args.logdir):
117 | os.makedirs(args.logdir)
118 | device = torch.device('cuda')
119 | # model
120 | model = HDRFlow()
121 | if args.init_weights:
122 | init_parameters(model)
123 | hdrflow_loss = HDRFlow_Loss_3E().to(device)
124 | # optimizer
125 | optimizer = torch.optim.Adam(model.parameters(), lr=args.lr, betas=(0.9, 0.999), eps=1e-08)
126 | model.to(device)
127 | model = nn.DataParallel(model)
128 |
129 | if args.resume:
130 | if os.path.isfile(args.resume):
131 | print("===> Loading checkpoint from: {}".format(args.resume))
132 | checkpoint = torch.load(args.resume)
133 | args.start_epoch = checkpoint['epoch']
134 | model.load_state_dict(checkpoint['state_dict'])
135 | optimizer.load_state_dict(checkpoint['optimizer'])
136 | print("===> Loaded checkpoint: epoch {}".format(checkpoint['epoch']))
137 | else:
138 | print("===> No checkpoint is founded at {}.".format(args.resume))
139 |
140 | train_loader, val_loader = fetch_dataloader(args)
141 |
142 | for epoch in range(args.epochs):
143 | adjust_learning_rate(args, optimizer, epoch)
144 | train(args, model, device, train_loader, optimizer, epoch, hdrflow_loss)
145 | validation(args, model, device, val_loader, optimizer, epoch)
146 |
147 | if __name__ == '__main__':
148 | main()
149 |
--------------------------------------------------------------------------------
/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenImagingLab/HDRFlow/c891759c5eba11869a038679da444e78c54f5a22/utils/__init__.py
--------------------------------------------------------------------------------
/utils/flow_viz.py:
--------------------------------------------------------------------------------
1 | # Flow visualization code used from https://github.com/tomrunia/OpticalFlow_Visualization
2 |
3 |
4 | # MIT License
5 | #
6 | # Copyright (c) 2018 Tom Runia
7 | #
8 | # Permission is hereby granted, free of charge, to any person obtaining a copy
9 | # of this software and associated documentation files (the "Software"), to deal
10 | # in the Software without restriction, including without limitation the rights
11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | # copies of the Software, and to permit persons to whom the Software is
13 | # furnished to do so, subject to conditions.
14 | #
15 | # Author: Tom Runia
16 | # Date Created: 2018-08-03
17 |
18 | import numpy as np
19 |
20 | def make_colorwheel():
21 | """
22 | Generates a color wheel for optical flow visualization as presented in:
23 | Baker et al. "A Database and Evaluation Methodology for Optical Flow" (ICCV, 2007)
24 | URL: http://vision.middlebury.edu/flow/flowEval-iccv07.pdf
25 |
26 | Code follows the original C++ source code of Daniel Scharstein.
27 | Code follows the the Matlab source code of Deqing Sun.
28 |
29 | Returns:
30 | np.ndarray: Color wheel
31 | """
32 |
33 | RY = 15
34 | YG = 6
35 | GC = 4
36 | CB = 11
37 | BM = 13
38 | MR = 6
39 |
40 | ncols = RY + YG + GC + CB + BM + MR
41 | colorwheel = np.zeros((ncols, 3))
42 | col = 0
43 |
44 | # RY
45 | colorwheel[0:RY, 0] = 255
46 | colorwheel[0:RY, 1] = np.floor(255*np.arange(0,RY)/RY)
47 | col = col+RY
48 | # YG
49 | colorwheel[col:col+YG, 0] = 255 - np.floor(255*np.arange(0,YG)/YG)
50 | colorwheel[col:col+YG, 1] = 255
51 | col = col+YG
52 | # GC
53 | colorwheel[col:col+GC, 1] = 255
54 | colorwheel[col:col+GC, 2] = np.floor(255*np.arange(0,GC)/GC)
55 | col = col+GC
56 | # CB
57 | colorwheel[col:col+CB, 1] = 255 - np.floor(255*np.arange(CB)/CB)
58 | colorwheel[col:col+CB, 2] = 255
59 | col = col+CB
60 | # BM
61 | colorwheel[col:col+BM, 2] = 255
62 | colorwheel[col:col+BM, 0] = np.floor(255*np.arange(0,BM)/BM)
63 | col = col+BM
64 | # MR
65 | colorwheel[col:col+MR, 2] = 255 - np.floor(255*np.arange(MR)/MR)
66 | colorwheel[col:col+MR, 0] = 255
67 | return colorwheel
68 |
69 |
70 | def flow_uv_to_colors(u, v, convert_to_bgr=False):
71 | """
72 | Applies the flow color wheel to (possibly clipped) flow components u and v.
73 |
74 | According to the C++ source code of Daniel Scharstein
75 | According to the Matlab source code of Deqing Sun
76 |
77 | Args:
78 | u (np.ndarray): Input horizontal flow of shape [H,W]
79 | v (np.ndarray): Input vertical flow of shape [H,W]
80 | convert_to_bgr (bool, optional): Convert output image to BGR. Defaults to False.
81 |
82 | Returns:
83 | np.ndarray: Flow visualization image of shape [H,W,3]
84 | """
85 | flow_image = np.zeros((u.shape[0], u.shape[1], 3), np.uint8)
86 | colorwheel = make_colorwheel() # shape [55x3]
87 | ncols = colorwheel.shape[0]
88 | rad = np.sqrt(np.square(u) + np.square(v))
89 | a = np.arctan2(-v, -u)/np.pi
90 | fk = (a+1) / 2*(ncols-1)
91 | k0 = np.floor(fk).astype(np.int32)
92 | k1 = k0 + 1
93 | k1[k1 == ncols] = 0
94 | f = fk - k0
95 | for i in range(colorwheel.shape[1]):
96 | tmp = colorwheel[:,i]
97 | col0 = tmp[k0] / 255.0
98 | col1 = tmp[k1] / 255.0
99 | col = (1-f)*col0 + f*col1
100 | idx = (rad <= 1)
101 | col[idx] = 1 - rad[idx] * (1-col[idx])
102 | col[~idx] = col[~idx] * 0.75 # out of range
103 | # Note the 2-i => BGR instead of RGB
104 | ch_idx = 2-i if convert_to_bgr else i
105 | flow_image[:,:,ch_idx] = np.floor(255 * col)
106 | return flow_image
107 |
108 |
109 | def flow_to_image(flow_uv, clip_flow=None, convert_to_bgr=False):
110 | """
111 | Expects a two dimensional flow image of shape.
112 |
113 | Args:
114 | flow_uv (np.ndarray): Flow UV image of shape [H,W,2]
115 | clip_flow (float, optional): Clip maximum of flow values. Defaults to None.
116 | convert_to_bgr (bool, optional): Convert output image to BGR. Defaults to False.
117 |
118 | Returns:
119 | np.ndarray: Flow visualization image of shape [H,W,3]
120 | """
121 | assert flow_uv.ndim == 3, 'input flow must have three dimensions'
122 | assert flow_uv.shape[2] == 2, 'input flow must have shape [H,W,2]'
123 | if clip_flow is not None:
124 | flow_uv = np.clip(flow_uv, 0, clip_flow)
125 | u = flow_uv[:,:,0]
126 | v = flow_uv[:,:,1]
127 | rad = np.sqrt(np.square(u) + np.square(v))
128 | rad_max = np.max(rad)
129 | epsilon = 1e-5
130 | u = u / (rad_max + epsilon)
131 | v = v / (rad_max + epsilon)
132 | return flow_uv_to_colors(u, v, convert_to_bgr)
--------------------------------------------------------------------------------
/utils/frame_utils.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from PIL import Image
3 | from os.path import *
4 | import re
5 |
6 | import cv2
7 | cv2.setNumThreads(0)
8 | cv2.ocl.setUseOpenCL(False)
9 |
10 | TAG_CHAR = np.array([202021.25], np.float32)
11 |
12 | def readFlow(fn):
13 | """ Read .flo file in Middlebury format"""
14 | # Code adapted from:
15 | # http://stackoverflow.com/questions/28013200/reading-middlebury-flow-files-with-python-bytes-array-numpy
16 |
17 | # WARNING: this will work on little-endian architectures (eg Intel x86) only!
18 | # print 'fn = %s'%(fn)
19 | with open(fn, 'rb') as f:
20 | magic = np.fromfile(f, np.float32, count=1)
21 | if 202021.25 != magic:
22 | print('Magic number incorrect. Invalid .flo file')
23 | return None
24 | else:
25 | w = np.fromfile(f, np.int32, count=1)
26 | h = np.fromfile(f, np.int32, count=1)
27 | # print 'Reading %d x %d flo file\n' % (w, h)
28 | data = np.fromfile(f, np.float32, count=2*int(w)*int(h))
29 | # Reshape data into 3D array (columns, rows, bands)
30 | # The reshape here is for visualization, the original code is (w,h,2)
31 | return np.resize(data, (int(h), int(w), 2))
32 |
33 | def readPFM(file):
34 | file = open(file, 'rb')
35 |
36 | color = None
37 | width = None
38 | height = None
39 | scale = None
40 | endian = None
41 |
42 | header = file.readline().rstrip()
43 | if header == b'PF':
44 | color = True
45 | elif header == b'Pf':
46 | color = False
47 | else:
48 | raise Exception('Not a PFM file.')
49 |
50 | dim_match = re.match(rb'^(\d+)\s(\d+)\s$', file.readline())
51 | if dim_match:
52 | width, height = map(int, dim_match.groups())
53 | else:
54 | raise Exception('Malformed PFM header.')
55 |
56 | scale = float(file.readline().rstrip())
57 | if scale < 0: # little-endian
58 | endian = '<'
59 | scale = -scale
60 | else:
61 | endian = '>' # big-endian
62 |
63 | data = np.fromfile(file, endian + 'f')
64 | shape = (height, width, 3) if color else (height, width)
65 |
66 | data = np.reshape(data, shape)
67 | data = np.flipud(data)
68 | return data
69 |
70 | def writeFlow(filename,uv,v=None):
71 | """ Write optical flow to file.
72 |
73 | If v is None, uv is assumed to contain both u and v channels,
74 | stacked in depth.
75 | Original code by Deqing Sun, adapted from Daniel Scharstein.
76 | """
77 | nBands = 2
78 |
79 | if v is None:
80 | assert(uv.ndim == 3)
81 | assert(uv.shape[2] == 2)
82 | u = uv[:,:,0]
83 | v = uv[:,:,1]
84 | else:
85 | u = uv
86 |
87 | assert(u.shape == v.shape)
88 | height,width = u.shape
89 | f = open(filename,'wb')
90 | # write the header
91 | f.write(TAG_CHAR)
92 | np.array(width).astype(np.int32).tofile(f)
93 | np.array(height).astype(np.int32).tofile(f)
94 | # arrange into matrix form
95 | tmp = np.zeros((height, width*nBands))
96 | tmp[:,np.arange(width)*2] = u
97 | tmp[:,np.arange(width)*2 + 1] = v
98 | tmp.astype(np.float32).tofile(f)
99 | f.close()
100 |
101 |
102 | def readFlowKITTI(filename):
103 | flow = cv2.imread(filename, cv2.IMREAD_ANYDEPTH|cv2.IMREAD_COLOR)
104 | flow = flow[:,:,::-1].astype(np.float32)
105 | flow, valid = flow[:, :, :2], flow[:, :, 2]
106 | flow = (flow - 2**15) / 64.0
107 | return flow, valid
108 |
109 | def readDispKITTI(filename):
110 | disp = cv2.imread(filename, cv2.IMREAD_ANYDEPTH) / 256.0
111 | valid = disp > 0.0
112 | flow = np.stack([-disp, np.zeros_like(disp)], -1)
113 | return flow, valid
114 |
115 |
116 | def writeFlowKITTI(filename, uv):
117 | uv = 64.0 * uv + 2**15
118 | valid = np.ones([uv.shape[0], uv.shape[1], 1])
119 | uv = np.concatenate([uv, valid], axis=-1).astype(np.uint16)
120 | cv2.imwrite(filename, uv[..., ::-1])
121 |
122 |
123 | def read_gen(file_name, pil=False):
124 | ext = splitext(file_name)[-1]
125 | if ext == '.png' or ext == '.jpeg' or ext == '.ppm' or ext == '.jpg':
126 | return Image.open(file_name)
127 | elif ext == '.bin' or ext == '.raw':
128 | return np.load(file_name)
129 | elif ext == '.flo':
130 | return readFlow(file_name).astype(np.float32)
131 | elif ext == '.pfm':
132 | flow = readPFM(file_name).astype(np.float32)
133 | if len(flow.shape) == 2:
134 | return flow
135 | else:
136 | return flow[:, :, :-1]
137 | return []
--------------------------------------------------------------------------------
/utils/utils.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import os, glob
3 | import cv2
4 | import math
5 | import imageio
6 | from math import log10
7 | import random
8 | import torch
9 | import torch.nn as nn
10 | import torch.nn.init as init
11 | from skimage.metrics import peak_signal_noise_ratio
12 | from skimage.transform import resize, rotate
13 | import torch.nn.functional as F
14 |
15 | def list_all_files_sorted(folder_name, extension=""):
16 | return sorted(glob.glob(os.path.join(folder_name, "*" + extension)))
17 |
18 | def read_expo_times(file_name):
19 | return np.power(2, np.loadtxt(file_name))
20 |
21 | def read_images(file_names):
22 | imgs = []
23 | for img_str in file_names:
24 | img = cv2.imread(img_str, -1)
25 | # equivalent to im2single from Matlab
26 | img = img / 2 ** 16
27 | img = np.float32(img)
28 | img.clip(0, 1)
29 | imgs.append(img)
30 | return np.array(imgs)
31 |
32 | def read_label(file_path, file_name):
33 | label = imageio.imread(os.path.join(file_path, file_name), 'hdr')
34 | label = label[:, :, [2, 1, 0]] ##cv2
35 | return label
36 |
37 | def ldr_to_hdr(img, expo, gamma=2.2):
38 | return (img ** gamma) / (expo + 1e-8)
39 |
40 | # def hdr_to_ldr(img, expo, gamma=2.2, stdv1=1e-3, stdv2=1e-3):
41 | def hdr_to_ldr(img, expo, gamma=2.2, stdv1=1e-3, stdv2=1e-3):
42 | # add noise to low expo
43 | if expo == 1. or expo == 4.:
44 | stdv = np.random.rand(*img.shape) * (stdv2 - stdv1) + stdv1
45 | noise = np.random.normal(0, stdv)
46 | img = (img + noise).clip(0, 1)
47 | img = np.power(img * expo, 1.0 / gamma)
48 | img = img.clip(0, 1)
49 | return img
50 |
51 | def tonemap(x):
52 | return (np.log(1 + 5000 * x)) / np.log(1 + 5000)
53 |
54 | def range_compressor_cuda(hdr_img, mu=5000):
55 | return (torch.log(1 + mu * hdr_img)) / math.log(1 + mu)
56 |
57 | def range_compressor_tensor(x, device):
58 | a = torch.tensor(1.0, device=device, requires_grad=False)
59 | mu = torch.tensor(5000.0, device=device, requires_grad=False)
60 | return (torch.log(a + mu * x)) / torch.log(a + mu)
61 |
62 | # def psnr(x, target):
63 | # sqrdErr = np.mean((x - target) ** 2)
64 | # return 10 * log10(1/sqrdErr)
65 |
66 | def batch_psnr(img, imclean, data_range):
67 | Img = img.data.cpu().numpy().astype(np.float32)
68 | Iclean = imclean.data.cpu().numpy().astype(np.float32)
69 | psnr = 0
70 | for i in range(Img.shape[0]):
71 | psnr += peak_signal_noise_ratio(Iclean[i,:,:,:], Img[i,:,:,:], data_range=data_range)
72 | return (psnr/Img.shape[0])
73 |
74 | def batch_psnr_mu(img, imclean, data_range):
75 | img = range_compressor_cuda(img)
76 | imclean = range_compressor_cuda(imclean)
77 | Img = img.data.cpu().numpy().astype(np.float32)
78 | Iclean = imclean.data.cpu().numpy().astype(np.float32)
79 | psnr = 0
80 | for i in range(Img.shape[0]):
81 | psnr += peak_signal_noise_ratio(Iclean[i,:,:,:], Img[i,:,:,:], data_range=data_range)
82 | return (psnr/Img.shape[0])
83 |
84 | # def adjust_learning_rate(args, optimizer, epoch):
85 | # lr = args.lr * (0.5 ** (epoch // args.lr_decay_interval))
86 | # for param_group in optimizer.param_groups:
87 | # param_group['lr'] = lr
88 |
89 | def adjust_learning_rate(args, optimizer, epoch):
90 | splits = args.lr_decay_epochs.split(':')
91 | assert len(splits) == 2
92 |
93 | # parse the epochs to downscale the learning rate (before :)
94 | downscale_epochs = [int(eid_str) for eid_str in splits[0].split(',')]
95 | downscale_rate = float(splits[1])
96 | print("downscale epochs: {}, downscale rate: {}".format(downscale_epochs, downscale_rate))
97 |
98 | lr = args.lr
99 | for eid in downscale_epochs:
100 | if epoch >= eid:
101 | lr /= downscale_rate
102 | else:
103 | break
104 | print("setting learning rate to {}".format(lr))
105 | for param_group in optimizer.param_groups:
106 | param_group['lr'] = lr
107 |
108 | def init_parameters(net):
109 | """Init layer parameters"""
110 | for m in net.modules():
111 | if isinstance(m, nn.Conv2d):
112 | init.kaiming_normal_(m.weight, mode='fan_out')
113 | if m.bias is not None:
114 | init.constant_(m.bias, 0)
115 | elif isinstance(m, nn.BatchNorm2d):
116 | init.constant_(m.weight, 1)
117 | init.constant_(m.bias, 0)
118 | elif isinstance(m, nn.Linear):
119 | init.xavier_normal_(m.weight)
120 | init.constant_(m.bias, 0)
121 |
122 | def set_random_seed(seed):
123 | """Set random seed for reproduce"""
124 | random.seed(seed)
125 | np.random.seed(seed)
126 | torch.manual_seed(seed)
127 | torch.cuda.manual_seed_all(seed)
128 | # torch.backends.cudnn.deterministic = True
129 | # torch.backends.cudnn.benchmark = False
130 |
131 | class AverageMeter(object):
132 | """Computes and stores the average and current value"""
133 | def __init__(self):
134 | self.reset()
135 |
136 | def reset(self):
137 | self.val = 0
138 | self.avg = 0
139 | self.sum = 0
140 | self.count = 0
141 |
142 | def update(self, val, n=1):
143 | self.val = val
144 | self.sum += val * n
145 | self.count += n
146 | self.avg = self.sum / self.count
147 |
148 | # def ssim(img1, img2):
149 | # C1 = (0.01 * 255)**2
150 | # C2 = (0.03 * 255)**2
151 |
152 | # img1 = img1.astype(np.float64)
153 | # img2 = img2.astype(np.float64)
154 | # kernel = cv2.getGaussianKernel(11, 1.5)
155 | # window = np.outer(kernel, kernel.transpose())
156 |
157 | # mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5] # valid
158 | # mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5]
159 | # mu1_sq = mu1**2
160 | # mu2_sq = mu2**2
161 | # mu1_mu2 = mu1 * mu2
162 | # sigma1_sq = cv2.filter2D(img1**2, -1, window)[5:-5, 5:-5] - mu1_sq
163 | # sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq
164 | # sigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2
165 |
166 | # ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) *
167 | # (sigma1_sq + sigma2_sq + C2))
168 | # return ssim_map.mean()
169 |
170 | def calculate_ssim(img1, img2):
171 | """
172 | calculate SSIM
173 |
174 | :param img1: [0, 255]
175 | :param img2: [0, 255]
176 | :return:
177 | """
178 | if not img1.shape == img2.shape:
179 | raise ValueError('Input images must have the same dimensions.')
180 | if img1.ndim == 2:
181 | return ssim(img1, img2)
182 | elif img1.ndim == 3:
183 | if img1.shape[2] == 3:
184 | ssims = []
185 | for i in range(3):
186 | ssims.append(ssim(img1, img2))
187 | return np.array(ssims).mean()
188 | elif img1.shape[2] == 1:
189 | return ssim(np.squeeze(img1), np.squeeze(img2))
190 | else:
191 | raise ValueError('Wrong input image dimensions.')
192 |
193 | def radiance_writer(out_path, image):
194 |
195 | with open(out_path, "wb") as f:
196 | f.write(b"#?RADIANCE\n# Made with Python & Numpy\nFORMAT=32-bit_rle_rgbe\n\n")
197 | f.write(b"-Y %d +X %d\n" %(image.shape[0], image.shape[1]))
198 |
199 | brightest = np.maximum(np.maximum(image[...,0], image[...,1]), image[...,2])
200 | mantissa = np.zeros_like(brightest)
201 | exponent = np.zeros_like(brightest)
202 | np.frexp(brightest, mantissa, exponent)
203 | scaled_mantissa = mantissa * 255.0 / brightest
204 | rgbe = np.zeros((image.shape[0], image.shape[1], 4), dtype=np.uint8)
205 | rgbe[...,0:3] = np.around(image[...,0:3] * scaled_mantissa[...,None])
206 | rgbe[...,3] = np.around(exponent + 128)
207 |
208 | rgbe.flatten().tofile(f)
209 |
210 | def save_hdr(path, image):
211 | return radiance_writer(path, image)
212 |
213 | def read_16bit_tif(img_name, crf=None):
214 | img = cv2.imread(img_name, -1) #/ 65535.0 # normalize to [0, 1]
215 | img = img[:, :, [2, 1, 0]] # BGR to RGB
216 | if crf is not None:
217 | img = reverse_crf(img, crf)
218 | img = img / crf.max()
219 | else:
220 | img = img / 65535.0
221 | return img
222 |
223 | def prob_center_crop(imgs):
224 | if np.random.uniform() > 0.9:
225 | return imgs
226 | new_imgs = []
227 | for img in imgs:
228 | h, w, c = img.shape
229 | t, l = h // 4, w // 6
230 | new_imgs.append(img[t:, l:w-l])
231 | return new_imgs
232 |
233 | def random_crop(inputs, size, margin=0):
234 | is_list = type(inputs) == list
235 | if not is_list: inputs = [inputs]
236 |
237 | outputs = []
238 | h, w, _ = inputs[0].shape
239 | c_h, c_w = size
240 | if h != c_h or w != c_w:
241 | t = random.randint(0+margin, h - c_h-margin)
242 | l = random.randint(0+margin, w - c_w-margin)
243 | for img in inputs:
244 | outputs.append(img[t: t+c_h, l: l+c_w])
245 | else:
246 | outputs = inputs
247 | if not is_list: outputs = outputs[0]
248 | return outputs
249 |
250 | def random_flip_lrud(inputs):
251 | if np.random.random() > 0.5:
252 | return inputs
253 | is_list = type(inputs) == list
254 | if not is_list: inputs = [inputs]
255 |
256 | outputs = []
257 | vertical_flip = True if np.random.random() > 0.5 else False # vertical flip
258 | for img in inputs:
259 | flip_img = np.fliplr(img)
260 | if vertical_flip:
261 | flip_img = np.flipud(flip_img)
262 | outputs.append(flip_img.copy())
263 | if not is_list: outputs = outputs[0]
264 | return outputs
265 |
266 | def random_rotate(inputs, angle=90.0):
267 | is_list = type(inputs) == list
268 | if not is_list: inputs = [inputs]
269 |
270 | outputs = []
271 | ang = np.random.uniform(0, angle)
272 | for img in inputs:
273 | outputs.append(rotate(img, angle=ang, mode='constant'))
274 | if not is_list: outputs = outputs[0]
275 | return outputs
276 |
277 | def read_list(list_path,ignore_head=False, sort=False):
278 | lists = []
279 | with open(list_path) as f:
280 | lists = f.read().splitlines()
281 | if ignore_head:
282 | lists = lists[1:]
283 | if sort:
284 | lists.sort(key=natural_keys)
285 | return lists
286 |
287 | def read_hdr(filename, use_cv2=True):
288 | ext = os.path.splitext(filename)[1]
289 | if use_cv2:
290 | hdr = cv2.imread(filename, -1)[:,:,::-1].clip(0)
291 | elif ext == '.exr':
292 | hdr = read_exr(filename)
293 | elif ext == '.hdr':
294 | hdr = cv2.imread(filename, -1)
295 | elif ext == '.npy':
296 | hdr = np.load(filenmae)
297 | else:
298 | raise_not_defined()
299 | return hdr
300 |
301 | class InputPadder:
302 | """ Pads images such that dimensions are divisible by 8 """
303 | def __init__(self, dims, mode='sintel', divis_by=8):
304 | self.ht, self.wd = dims[-2:]
305 | pad_ht = (((self.ht // divis_by) + 1) * divis_by - self.ht) % divis_by
306 | pad_wd = (((self.wd // divis_by) + 1) * divis_by - self.wd) % divis_by
307 | self._pad = [pad_wd//2, pad_wd - pad_wd//2, pad_ht//2, pad_ht - pad_ht//2]
308 |
309 | def pad(self, inputs):
310 | return [F.pad(x, self._pad, mode='replicate') for x in inputs]
311 |
312 | def unpad(self,x):
313 | ht, wd = x.shape[-2:]
314 | c = [self._pad[2], ht-self._pad[3], self._pad[0], wd-self._pad[1]]
315 | return x[..., c[0]:c[1], c[2]:c[3]]
316 |
317 | def coords_grid(b, h, w, device):
318 | coords = torch.meshgrid(torch.arange(h, device=device), torch.arange(w, device=device))
319 | coords = torch.stack(coords[::-1], dim=0).float()
320 | return coords[None].repeat(b, 1, 1, 1)
321 |
322 | def backward_warp(img, flow, pad='zeros'):
323 | b, c, h, w = img.shape
324 | grid = coords_grid(b, h, w, device=img.device)
325 | grid = grid + flow
326 | xgrid, ygrid = grid.split([1,1], dim=1)
327 | xgrid = 2*xgrid/(w-1) - 1
328 | ygrid = 2*ygrid/(h-1) - 1
329 | grid = torch.cat([xgrid, ygrid], dim=1)
330 |
331 | warped_img = F.grid_sample(input=img, grid=grid.permute(0,2,3,1), mode='bilinear', padding_mode='zeros')
332 | return warped_img
333 |
334 | def reverse_crf(img, crf):
335 | img = img.astype(int)
336 | out = img.astype(float)
337 | for i in range(img.shape[2]):
338 | out[:,:,i] = crf[:,i][img[:,:,i]] # crf shape [65536, 3]
339 | return out
340 |
341 | # For online global alignment
342 | def cvt_MToTheta(M, w, h):
343 | M_aug = np.concatenate([M, np.zeros((1, 3))], axis=0)
344 | M_aug[-1, -1] = 1.0
345 | N = get_N(w, h)
346 | N_inv = get_N_inv(w, h)
347 | theta = N @ M_aug @ N_inv
348 | theta = np.linalg.inv(theta)
349 | return theta[:2, :]
350 |
351 | def get_N(W, H):
352 | """N that maps from unnormalized to normalized coordinates"""
353 | N = np.zeros((3, 3), dtype=np.float64)
354 | N[0, 0] = 2.0 / W
355 | N[0, 1] = 0
356 | N[1, 1] = 2.0 / H
357 | N[1, 0] = 0
358 | N[0, -1] = -1.0
359 | N[1, -1] = -1.0
360 | N[-1, -1] = 1.0
361 | return N
362 |
363 | def get_N_inv(W, H):
364 | """N that maps from normalized to unnormalized coordinates"""
365 | # TODO: do this analytically maybe?
366 | N = get_N(W, H)
367 | return np.linalg.inv(N)
368 |
369 | def apply_gamma(image, gamma=2.2):
370 | image = image.clip(1e-8, 1)
371 | image = np.power(image, 1.0 / gamma)
372 | return image
373 |
374 | def affine_warp(img, theta): # warp img1 to img2
375 | n, c, h, w = img.shape
376 | affine_grid = F.affine_grid(theta, img.shape)
377 | invalid_mask = ((affine_grid.narrow(3, 0, 1).abs() > 1) + (affine_grid.narrow(3, 1, 1).abs() > 1)) >= 1
378 | invalid_mask = invalid_mask.view(n, 1, h, w).float()
379 | img1_to_img2 = F.grid_sample(img, affine_grid)
380 | img1_to_img2 = img * invalid_mask + img1_to_img2 * (1 - invalid_mask)
381 | return img1_to_img2
382 |
383 | def global_align_nbr_ldrs(ldrs, matches):
384 | if len(ldrs) == 3:
385 | match_p = matches[0][:,1].view(-1, 2, 3)
386 | match_n = matches[2][:,0].view(-1, 2, 3)
387 | p_to_c = affine_warp(ldrs[0], match_p)
388 | n_to_c = affine_warp(ldrs[2], match_n)
389 | return [p_to_c, ldrs[1], n_to_c]
390 |
391 | elif len(ldrs) == 5:
392 | match_p2 = matches[0][:,3].view(-1, 2, 3)
393 | match_p1 = matches[1][:,2].view(-1, 2, 3)
394 | match_n1 = matches[3][:,1].view(-1, 2, 3)
395 | match_n2 = matches[4][:,0].view(-1, 2, 3)
396 | p2_to_c = affine_warp(ldrs[0], match_p2)
397 | p1_to_c = affine_warp(ldrs[1], match_p1)
398 | n1_to_c = affine_warp(ldrs[3], match_n1)
399 | n2_to_c = affine_warp(ldrs[4], match_n2)
400 | return [p2_to_c, p1_to_c, ldrs[2], n1_to_c, n2_to_c]
401 | else:
402 | return 0
403 |
--------------------------------------------------------------------------------