├── .gitignore ├── LICENSE ├── README.md ├── db.sqlite3 ├── lanczos.py ├── multilum.py ├── probe_predict ├── eval.py └── model.py ├── probepred_eval.sh ├── pylintrc ├── relight ├── eval.py └── model.py ├── relight_eval.sh ├── scenes.json ├── setup.py └── tests ├── data ├── everett_kitchen2 │ ├── dir_0_mip6.jpg │ ├── dir_10_mip6.jpg │ ├── dir_11_mip6.jpg │ ├── dir_12_mip6.jpg │ ├── dir_13_mip6.jpg │ ├── dir_14_mip6.jpg │ ├── dir_15_mip6.jpg │ ├── dir_16_mip6.jpg │ ├── dir_17_mip6.jpg │ ├── dir_18_mip6.jpg │ ├── dir_19_mip6.jpg │ ├── dir_1_mip6.jpg │ ├── dir_20_mip6.jpg │ ├── dir_21_mip6.jpg │ ├── dir_22_mip6.jpg │ ├── dir_23_mip6.jpg │ ├── dir_24_mip6.jpg │ ├── dir_2_mip6.jpg │ ├── dir_3_mip6.jpg │ ├── dir_4_mip6.jpg │ ├── dir_5_mip6.jpg │ ├── dir_6_mip6.jpg │ ├── dir_7_mip6.jpg │ ├── dir_8_mip6.jpg │ ├── dir_9_mip6.jpg │ ├── materials_mip4.png │ ├── probes │ │ ├── dir_0_chrome32.jpg │ │ ├── dir_10_chrome32.jpg │ │ ├── dir_11_chrome32.jpg │ │ ├── dir_12_chrome32.jpg │ │ ├── dir_13_chrome32.jpg │ │ ├── dir_14_chrome32.jpg │ │ ├── dir_15_chrome32.jpg │ │ ├── dir_16_chrome32.jpg │ │ ├── dir_17_chrome32.jpg │ │ ├── dir_18_chrome32.jpg │ │ ├── dir_19_chrome32.jpg │ │ ├── dir_1_chrome32.jpg │ │ ├── dir_20_chrome32.jpg │ │ ├── dir_21_chrome32.jpg │ │ ├── dir_22_chrome32.jpg │ │ ├── dir_23_chrome32.jpg │ │ ├── dir_24_chrome32.jpg │ │ ├── dir_2_chrome32.jpg │ │ ├── dir_3_chrome32.jpg │ │ ├── dir_4_chrome32.jpg │ │ ├── dir_5_chrome32.jpg │ │ ├── dir_6_chrome32.jpg │ │ ├── dir_7_chrome32.jpg │ │ ├── dir_8_chrome32.jpg │ │ └── dir_9_chrome32.jpg │ └── thumb.jpg └── everett_kitchen2_golden │ ├── dir_0_mip7.jpg │ ├── dir_10_mip7.jpg │ ├── dir_11_mip7.jpg │ ├── dir_12_mip7.jpg │ ├── dir_13_mip7.jpg │ ├── dir_14_mip7.jpg │ ├── dir_15_mip7.jpg │ ├── dir_16_mip7.jpg │ ├── dir_17_mip7.jpg │ ├── dir_18_mip7.jpg │ ├── dir_19_mip7.jpg │ ├── dir_1_mip7.jpg │ ├── dir_20_mip7.jpg │ ├── dir_21_mip7.jpg │ ├── dir_22_mip7.jpg │ ├── dir_23_mip7.jpg │ ├── dir_24_mip7.jpg │ ├── dir_2_mip7.jpg │ ├── dir_3_mip7.jpg │ ├── dir_4_mip7.jpg │ ├── dir_5_mip7.jpg │ ├── dir_6_mip7.jpg │ ├── dir_7_mip7.jpg │ ├── dir_8_mip7.jpg │ ├── dir_9_mip7.jpg │ └── probes │ ├── dir_0_chrome16.jpg │ ├── dir_10_chrome16.jpg │ ├── dir_11_chrome16.jpg │ ├── dir_12_chrome16.jpg │ ├── dir_13_chrome16.jpg │ ├── dir_14_chrome16.jpg │ ├── dir_15_chrome16.jpg │ ├── dir_16_chrome16.jpg │ ├── dir_17_chrome16.jpg │ ├── dir_18_chrome16.jpg │ ├── dir_19_chrome16.jpg │ ├── dir_1_chrome16.jpg │ ├── dir_20_chrome16.jpg │ ├── dir_21_chrome16.jpg │ ├── dir_22_chrome16.jpg │ ├── dir_23_chrome16.jpg │ ├── dir_24_chrome16.jpg │ ├── dir_2_chrome16.jpg │ ├── dir_3_chrome16.jpg │ ├── dir_4_chrome16.jpg │ ├── dir_5_chrome16.jpg │ ├── dir_6_chrome16.jpg │ ├── dir_7_chrome16.jpg │ ├── dir_8_chrome16.jpg │ └── dir_9_chrome16.jpg └── test_multilum.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | __pycache__ 3 | dist 4 | multilum.egg-info 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of 2 | this software and associated documentation files (the "Software"), to deal in 3 | the Software without restriction, including without limitation the rights to 4 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 5 | the Software, and to permit persons to whom the Software is furnished to do so, 6 | subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python SDK for "A Dataset of Multi-Illumination Images in the Wild" (ICCV 2019) 2 | 3 | The SDK provides access to image downloads, image resizing, pre-processed light probes, material masks, and scene meta data. For batch data download, paper download and more please visit https://projects.csail.mit.edu/illumination. 4 | 5 | 6 | Below are a example uses of the SDK. 7 | ``` 8 | Load subset of scenes like this 9 | I = query_images(['main_experiment120', 'kingston_dining10']) 10 | 11 | # Load all test images in low resolution 12 | I = query_images(test_scenes(), mip=5) 13 | 14 | # Load light direction 0 in HDR floating point 15 | I = query_images(test_scenes(), dirs=0, mip=5, hdr=True) 16 | 17 | # Get matching light probes 18 | P = query_probes(test_scenes()) 19 | 20 | # ... and material annotations 21 | M = query_materials(test_scenes(), mip=5) 22 | 23 | # List all kitchen scene meta data 24 | K = query_scenes(room_types=['kitchen']) 25 | 26 | # List all kitchen scenes in the training set 27 | K = query_scenes(train_scenes(), room_types=['kitchen']) 28 | 29 | # List all room types 30 | T = query_room_types() 31 | ``` 32 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/db.sqlite3 -------------------------------------------------------------------------------- /lanczos.py: -------------------------------------------------------------------------------- 1 | """Lanczos filter implementation (uint8, uint16, float32, float64). 2 | 3 | Evaluated alternatives: 4 | PIL has a lanczos filter but only for uint8. 5 | scipy.mis.imresize() silently converts float to uint8 and calls into PIL 6 | skimage only has lanczos upsampling but downsampling uses gaussian filter. 7 | 8 | The implementation below is reasonably efficient thanks to scipy sparse matrix 9 | multiply. For better performance this can be replaced with a specialized lanczos 10 | implementation such as Intel Integrated Performance Primitives. 11 | """ 12 | 13 | import argparse 14 | from math import floor, ceil 15 | 16 | import numpy as np 17 | import scipy 18 | from scipy.sparse import csr_matrix 19 | 20 | 21 | __all__ = ('resize_lanczos') 22 | 23 | def resize_lanczos(I, h, w): 24 | I = horizontal_lanczos(I, w) 25 | # we force the intermediate result after x-filter back into I.dtype. 26 | # this might lead to different roundoff error than an implementation that 27 | # stores the intermediate result as floats. 28 | I = np.swapaxes(I, 0, 1) 29 | I = horizontal_lanczos(I, h) 30 | return np.swapaxes(I, 0, 1) 31 | 32 | 33 | def lanczos2(x): 34 | """Evaluate the three-lobed (a=2) lanczos filter. The image filter 35 | precomputes filter weights once per image and reuses the same weights 36 | for each image row (horizontal filter) or column (vertical filter).""" 37 | a = 2 38 | ret = np.zeros_like(x) 39 | ret[x == 0] = 1 40 | ret[x != 0] = np.sinc(x[x != 0]) * np.sinc(x[x != 0] / a) 41 | ret[np.abs(x) >= a] = 0 42 | return ret 43 | 44 | def saturation_cast(I, dtype): 45 | """Utility for filtering integer images""" 46 | if dtype=='uint8': 47 | return np.clip(I, 0, 255).astype(dtype) 48 | elif dtype=='uint16': 49 | return np.clip(I, 0, 65535).astype(dtype) 50 | elif dtype in ['float32', 'float64']: 51 | return I.astype(dtype) 52 | else: 53 | raise ValueError("Saturation cast for type %s not implemented" % I.type) 54 | 55 | 56 | def horizontal_lanczos(I, w, comment=None): 57 | """Apply 3-lobed Lanczos filter along horizontal (x) dimension. 58 | 59 | Returned image has shape (I.shape[0], w, I.shape[2]) and same 60 | dtype as the input image. 61 | 62 | Set "comment" argument for debug information 63 | """ 64 | dt = I.dtype 65 | inh, inw = I.shape[:2] 66 | nchan = 1 if len(I.shape) == 2 else I.shape[2] 67 | winsz = 5 68 | if comment: 69 | from lum.tictoc import tictoc 70 | print("winsz is", winsz) 71 | 72 | 73 | if comment: 74 | print("compute %s filter" % comment) 75 | tic = tictoc() 76 | 77 | xs = inw/w 78 | xfltscale = max(1, xs) 79 | srange = np.arange(floor(-2*xfltscale), ceil(2*xfltscale)+1) 80 | 81 | # We treat image filtering as sparse linear matrix multiply R = M * A 82 | # with filter matrix M. We start by constructing the matrix. 83 | ntap = len(srange) 84 | row = np.zeros([w,ntap], dtype='int32') 85 | col = np.zeros([w,ntap], dtype='int32') 86 | data = np.zeros([w,ntap], dtype='float32') 87 | 88 | if comment: 89 | print("using %d tap filter" % len(srange)) 90 | 91 | for x in range(w): 92 | center = x * xs 93 | # it is important to always round down (or up) here 94 | # round-to-even will cause misaligned indices. 95 | # this become especially noticeable when upsampling 96 | # by integer factors 97 | r = (center + srange + 0.5).astype('int32') 98 | weights = lanczos2((r - center) / xfltscale) 99 | 100 | # handle image bounds, normalize filter weight 101 | # in order to handle out of bounds, we clamp the indices 102 | # to [0, w) and set the associated weight to zero. 103 | weights[r<0] = 0 104 | weights[r>=inw] = 0 105 | r[r<0] = 0 106 | r[r>=inw] = 0 107 | weights /= np.sum(weights) 108 | 109 | for ti, c in enumerate(r): 110 | row[x, ti] = x 111 | col[x, ti] = c 112 | data[x, ti] = weights[ti] 113 | 114 | row = np.reshape(row, [-1]) 115 | col = np.reshape(col, [-1]) 116 | data = np.reshape(data, [-1]) 117 | M = csr_matrix((data, (row, col)), shape=(w, inw)) 118 | 119 | if comment: 120 | print("took", tic.toc()) 121 | tic.tic() 122 | 123 | if comment: 124 | print("apply %s filter" % comment) 125 | 126 | # Construct right-hand side. R = M * A 127 | # Each column of A has one row of the input image. 128 | # A has (I.ncols * I.nchans) columns 129 | A = np.moveaxis(I, 1, 0) 130 | A = np.reshape(A, [inw, inh*nchan]) 131 | R = (M * scipy.sparse.csr_matrix(A)).toarray() 132 | 133 | # Reshape R into [h,w,c] image format 134 | R = np.reshape(R, [w, inh, nchan]) 135 | R = np.moveaxis(R, 1, 0) 136 | R = saturation_cast(R, dt) 137 | R = R.reshape([inh, w, nchan]) 138 | 139 | if comment: 140 | print("took", tic.toc()) 141 | return R 142 | 143 | def main(): 144 | """Test stub for development only""" 145 | from multilum import readexr, writeexr 146 | from lum import plt 147 | from lum import pfm 148 | 149 | parser = argparse.ArgumentParser() 150 | parser.add_argument("image") 151 | opts = parser.parse_args() 152 | 153 | if opts.image.endswith("jpg"): 154 | I = pfm.load_jpg3(opts.image, dtype="uint8") 155 | else: 156 | I = readexr(opts.image) 157 | 158 | writeexr(I, "outfsz.exr") 159 | Ism = resize_lanczos(I, 100, 150) 160 | writeexr(Ism, "out.exr") 161 | 162 | plt.gshow(Ism) 163 | plt.show() 164 | 165 | if __name__ == "__main__": 166 | main() 167 | 168 | -------------------------------------------------------------------------------- /multilum.py: -------------------------------------------------------------------------------- 1 | """Python SDK for "A Dataset of Multi-Illumination Images in the Wild" 2 | 3 | The SDK provides access to image downloads, image resizing, pre-processed light probes, 4 | material annotations, and scene meta data, such as room types. 5 | 6 | Below are a example uses of the SDK 7 | 8 | Load subset of scenes like this 9 | I = query_images(['main_experiment120', 'kingston_dining10']) 10 | 11 | Load all test images in low resolution 12 | I = query_images(test_scenes(), mip=5) 13 | 14 | Load light direction 0 in HDR floating point 15 | I = query_images(test_scenes(), dirs=[0], mip=5, hdr=True) 16 | 17 | Get matching light probes 18 | P = query_probes(test_scenes()) 19 | 20 | And material annotations 21 | M = query_materials(test_scenes(), mip=5) 22 | 23 | List all kitchen scenes 24 | K = query_scenes(room_types=['kitchen']) 25 | 26 | List all kitchen scenes in the training set 27 | K = query_scenes(train_scenes(), room_types=['kitchen']) 28 | 29 | List all room types 30 | T = query_room_types() 31 | 32 | Batch data download, paper download and more: 33 | https://projects.csail.mit.edu/illumination 34 | """ 35 | 36 | 37 | from collections.abc import Iterable 38 | from collections import namedtuple 39 | import io 40 | import json 41 | import os 42 | import urllib.request 43 | import zipfile 44 | 45 | import numpy as np 46 | import tqdm 47 | 48 | from lanczos import resize_lanczos 49 | 50 | # PIL and OpenEXR are imported lazily if needed 51 | 52 | 53 | # ____________ Repository-wide functions ____________ 54 | BASE_URL = "https://data.csail.mit.edu/multilum" 55 | basedir = os.path.join(os.environ["HOME"], ".multilum") 56 | 57 | def set_datapath(path): 58 | """Call this function before any other call to change the data directory. 59 | """ 60 | global basedir 61 | basedir = path 62 | 63 | 64 | # ____________ Image Query Functions ____________ 65 | # Directions where the flash is directly visible 66 | FRONTAL_DIRECTIONS = [2, 3, 19, 20, 21, 22, 24] 67 | 68 | # Directions where the flash is only visible indirectly 69 | NOFRONTAL_DIRECTIONS = [i for i in range(25) if i not in FRONTAL_DIRECTIONS] 70 | 71 | 72 | def query_images(scenes=None, dirs=None, *, mip=2, hdr=False): 73 | """Return a 5D tensor if images 74 | 75 | Args: 76 | scenes: list of scenes (name or object) or None for all scenes 77 | dirs: list of integer indices or None for all directions. Can use FRONTAL_DIRECTIONS and 78 | NOFRONAL_DIRECTIONS directions 79 | mip: mip level index. smaller mip levels mean larger images. It is recommended to work 80 | with mip=2 (1500x1000px) for most applications. Set to mip=0 for high resolution 81 | (6000x4000px) images. 82 | hdr: boolean flag that selects between 8-bit images or linear HDR images. 83 | 84 | Returns 85 | 5D numpy array with shape (num_scenes, num_dirs, height, width, 3). The dattype of the 86 | returned array is uint8 for hdr=False, float32 for hdr=True 87 | """ 88 | 89 | scenes = sanitize_scenes_arg(scenes) 90 | dirs = sanitize_dirs_arg(dirs) 91 | 92 | h, w = imshape(mip) 93 | ret = np.zeros([len(scenes), len(dirs), h, w, 3], dtype=dtype(hdr)) 94 | 95 | ensure_downloaded(scenes, mip=mip, hdr=hdr) 96 | for iscene, scene in enumerate(scenes): 97 | for idir, dir in enumerate(dirs): 98 | ret[iscene, idir] = readimage(impath(scene, dir, mip, hdr)) 99 | return ret 100 | 101 | def query_probes(scenes=None, dirs=None, material="chrome", *, size=256, hdr=False): 102 | """Return a 5D tensor if images 103 | 104 | Args: 105 | scenes: list of scenes (name or object) or None for all scenes 106 | dirs: list of integer indices or None for all directions. Can use FRONTAL_DIRECTIONS and 107 | NOFRONAL_DIRECTIONS directions 108 | material: "chrome" for images of the chrome ball. "gray" for plastic gray ball 109 | size(int): size in pixels that will be loaded 110 | hdr(bool): boolean flag that selects between 8-bit images or linear HDR images. 111 | 112 | Returns 113 | 5D numpy array with shape (num_scenes, num_dirs, size, size, 3). The dattype of the 114 | returned array is uint8 for hdr=False, float32 for hdr=True 115 | """ 116 | scenes = sanitize_scenes_arg(scenes) 117 | dirs = sanitize_dirs_arg(dirs) 118 | 119 | 120 | h, w = size, size 121 | 122 | ret = np.zeros([len(scenes), len(dirs), h, w, 3], dtype=dtype(hdr)) 123 | 124 | ensure_probes_downloaded(scenes, material=material, size=size, hdr=hdr) 125 | for iscene, scene in enumerate(scenes): 126 | for idir, dir in enumerate(dirs): 127 | ret[iscene, idir] = readimage(probepath(scene, dir, material, size, hdr)) 128 | return ret 129 | 130 | 131 | def query_materials(scenes=None, *, mip=2, apply_colors=False): 132 | """Return a numpy array of material masks 133 | 134 | Args: 135 | scenes: list of scenes (name or object) or None for all scenes 136 | mip: mip level index. smaller mip levels mean larger images. It is recommended to work 137 | with mip=2 (1500x1000px) for most applications. Set to mip=0 for high resolution 138 | (6000x4000px) images. 139 | apply_colors: If true, returns RGB masks as used in the paper. If false, returns scalar 140 | integer indices. 141 | 142 | Returns 143 | if apply_colors is False, returns 3D numpy array with shape (num_scenes, height, width). if 144 | apply_colors is True, returns 4D array with shape (num_scenes, height, width, 3). Returned array 145 | is always type uint8. 146 | """ 147 | scenes = sanitize_scenes_arg(scenes) 148 | 149 | h, w = imshape(mip) 150 | if apply_colors: 151 | shape = [len(scenes), h, w, 3] 152 | else: 153 | shape = [len(scenes), h, w] 154 | ret = np.zeros(shape, dtype='uint8') 155 | 156 | ensure_materials_downloaded(scenes, mip=mip) 157 | for iscene, scene in enumerate(scenes): 158 | ret[iscene] = read_material_image(material_impath(scene, mip=mip), apply_colors=apply_colors) 159 | return ret 160 | 161 | 162 | # ____________ Meta data functions ____________ 163 | Scene = namedtuple('Scene', ['name', 'room']) 164 | Room = namedtuple('Room', ['name', 'building', 'room_type']) 165 | RoomType = namedtuple('RoomType', ['name']) 166 | Building = namedtuple('Building', ['name']) 167 | 168 | _scene_json = None 169 | def all_scenes(): 170 | """List all scenes 171 | 172 | Returns: 173 | list of Scene objects 174 | """ 175 | 176 | global _scene_json 177 | if _scene_json is None: 178 | with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "scenes.json")) as fh: 179 | _scene_json = json.loads(fh.read()) 180 | 181 | ret = [] 182 | for s in _scene_json: 183 | ret.append(Scene(s["name"], 184 | Room(s["room"], 185 | Building(s["building"]), 186 | RoomType(s["room_type"])))) 187 | return ret 188 | 189 | def all_buildings(): 190 | """List all buildings 191 | 192 | Returns: 193 | list of Building objects 194 | """ 195 | retset = set() 196 | for scene in all_scenes(): 197 | retset.add(scene.room.building) 198 | return list(retset) 199 | 200 | def all_rooms(): 201 | """List all rooms 202 | 203 | Returns: 204 | list of Room objects 205 | """ 206 | retset = set() 207 | for scene in all_scenes(): 208 | retset.add(scene.room) 209 | return list(retset) 210 | 211 | def all_room_types(): 212 | """List all room types 213 | 214 | Returns: 215 | list of RoomType objects 216 | """ 217 | retset = set() 218 | for scene in all_scenes(): 219 | retset.add(scene.room.room_type) 220 | return list(retset) 221 | 222 | def query_scenes(scenes=None, *, buildings=None, rooms=None, room_types=None): 223 | """Query subset of scenes 224 | 225 | Args: 226 | scenes: list of scene names or scene objects 227 | buildings: list of building names or building objects 228 | rooms: list of room names "/" or room objects 229 | room_types: list of room types, e.g. ["kitchen", "basement"] 230 | 231 | Returns: 232 | list of Scene objects 233 | """ 234 | scene_names = {name(s) for s in sanitize_scenes_arg(scenes)} 235 | building_names = {name(b) for b in sanitize_buildings_arg(buildings)} 236 | room_type_names = {name(rt) for rt in sanitize_room_types_arg(room_types)} 237 | room_ids = set(sanitize_rooms_arg_to_ids(rooms)) 238 | 239 | ret = [] 240 | for scene in all_scenes(): 241 | scene_name, room_name, building_name, room_type_name = \ 242 | scene.name, scene.room.name, scene.room.building.name, scene.room.room_type.name 243 | if scene_name in scene_names and \ 244 | "%s/%s" % (building_name, room_name) in room_ids and \ 245 | building_name in building_names and \ 246 | room_type_name in room_type_names: 247 | ret.append(scene) 248 | return ret 249 | 250 | def query_buildings(buildings=None): 251 | """Query subset of buildings 252 | 253 | Args: 254 | buildings: list of building names or building objects, e.g. ["elm", "main"] 255 | 256 | Returns: 257 | list of Building objects 258 | """ 259 | building_names = {name(b) for b in sanitize_buildings_arg(buildings)} 260 | 261 | ret = [] 262 | for b in all_buildings(): 263 | if b.name in building_names: 264 | ret.append(b) 265 | return ret 266 | 267 | 268 | def query_rooms(rooms=None, *, room_types=None, buildings=None): 269 | """Query subset of rooms 270 | 271 | Args: 272 | rooms: list of room names, e.g. "everett/kitchen", or room objects 273 | buildings: list of building names or building objects, e.g. ["elm", "main"] 274 | room_types: list of room types, e.g. ["kitchen", "basement"] 275 | 276 | Returns: 277 | list of Room objects 278 | """ 279 | building_names = {name(b) for b in sanitize_buildings_arg(buildings)} 280 | room_type_names = {name(rt) for rt in sanitize_room_types_arg(room_types)} 281 | room_ids = set(sanitize_rooms_arg_to_ids(rooms)) 282 | 283 | ret = [] 284 | for r in all_rooms(): 285 | room_name, building_name, room_type_name = r.name, r.building.name, r.room_type.name 286 | if "%s/%s" % (building_name, room_name) in room_ids and \ 287 | building_name in building_names and \ 288 | room_type_name in room_type_names: 289 | ret.append(r) 290 | return ret 291 | 292 | def query_room_types(room_types=None): 293 | """Query subset of room types 294 | 295 | Args: 296 | room_types: list of room types, e.g. ["kitchen", "basement"] 297 | 298 | Returns: 299 | list of RoomType objects 300 | """ 301 | room_type_names = {name(rt) for rt in sanitize_room_types_arg(room_types)} 302 | 303 | ret = [] 304 | for rt in all_room_types(): 305 | if rt.name in room_type_names: 306 | ret.append(rt) 307 | return ret 308 | 309 | def test_scenes(): 310 | """Return all scenes of the test set""" 311 | return [s for s in all_scenes() if s.name.startswith('everett')] 312 | 313 | def train_scenes(): 314 | """Return all scenes of the training set""" 315 | return [s for s in all_scenes() if not s.name.startswith('everett')] 316 | 317 | 318 | 319 | 320 | 321 | 322 | ## =========== INTERNAL / ADVANCED FUNCTIONS ======================= ## 323 | 324 | # ____________ Image-level functions ____________ 325 | def readimage(path): 326 | """Generic image read helper""" 327 | if path.endswith("exr"): 328 | return readexr(path) 329 | elif path.endswith("jpg"): 330 | return readjpg(path) 331 | else: 332 | raise ValueError("Unrecognized file type for path %s" % path) 333 | 334 | def read_material_image(path, *, apply_colors=False): 335 | """Read material PNG mask 336 | 337 | Args: 338 | path: image path 339 | apply_colors: whether to apply the embedded color palette 340 | """ 341 | return readpng_indexed(path, apply_palette=apply_colors) 342 | 343 | def readjpg(path): 344 | """JPG read helper. Requires PIL.""" 345 | from PIL import Image 346 | return np.array(Image.open(path)) 347 | 348 | def readpng_indexed(path, *, apply_palette): 349 | """Indexed PNG read helper. Requires PIL.""" 350 | from PIL import Image 351 | 352 | im = Image.open(path) 353 | if not im.mode == "P": 354 | raise ValueError("Expected indexed PNG") 355 | 356 | if apply_palette: 357 | im = im.convert(mode="RGB") 358 | shape = (im.height, im.width, 3) 359 | else: 360 | shape = (im.height, im.width) 361 | 362 | npim = np.ndarray(shape=shape, dtype="uint8", buffer=im.tobytes()) 363 | # palette = np.ndarray(shape=[256,3], dtype="uint8", buffer=im.palette.getdata()[1]) 364 | return npim 365 | 366 | def readexr(path): 367 | """EXR read helper. Requires OpenEXR.""" 368 | import OpenEXR 369 | 370 | fh = OpenEXR.InputFile(path) 371 | dw = fh.header()['dataWindow'] 372 | w, h = (dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1) 373 | 374 | rgb = [np.ndarray([h,w], dtype="float32", buffer=fh.channel(c)) for c in ['R', 'G', 'B']] 375 | ret = np.zeros([h, w, 3], dtype='float32') 376 | for i in [0,1,2]: 377 | ret[:,:,i] = rgb[i] 378 | return ret 379 | 380 | def writeimage(I, path): 381 | """Generic image write helper""" 382 | if path.endswith("exr"): 383 | return writeexr(I, path) 384 | elif path.endswith("jpg"): 385 | return writejpg(I, path) 386 | else: 387 | raise ValueError("Unrecognized file type for path %s" % path) 388 | 389 | def writejpg(I, path): 390 | """JPG write helper. Requires PIL.""" 391 | 392 | from PIL import Image 393 | im = Image.fromarray(I) 394 | im.save(path, "JPEG", quality=95) 395 | 396 | def writeexr(I, path): 397 | """EXR write helper. Requires OpenEXR.""" 398 | import OpenEXR 399 | import Imath 400 | h, w = I.shape[:2] 401 | head = OpenEXR.Header(w, h) 402 | head["compression"] = Imath.Compression(Imath.Compression.DWAB_COMPRESSION) 403 | of = OpenEXR.OutputFile(path, head) 404 | R, G, B = [I[:,:,c].tobytes() for c in [0,1,2]] 405 | of.writePixels({'R': R, 'G': G, 'B': B}) 406 | 407 | 408 | def impath(scene, dir, mip, hdr): 409 | """Generate path for image 410 | 411 | Args: 412 | scene: scene name 413 | dir: direction number 414 | mip (int): mip level 415 | hdr (bool): generate path for HDR or not 416 | """ 417 | return os.path.join(basedir, name(scene), "dir_%d_mip%d.%s" % (dir, mip, ext(hdr))) 418 | 419 | def imshape(mip): 420 | """Compute image size for different mip levels""" 421 | return 4000 // 2 **mip, 6000 // 2 **mip 422 | 423 | 424 | def probepath(scene, dir, material, size, hdr): 425 | """Compute path for material math 426 | 427 | Args: 428 | scene: scene name 429 | mip (int): mip level 430 | """ 431 | return os.path.join(basedir, name(scene), 432 | "probes", "dir_%d_%s%d.%s" % (dir, material, size, ext(hdr))) 433 | 434 | def material_impath(scene, mip): 435 | """Generate path for material map of given scene / mip level""" 436 | return os.path.join(basedir, name(scene), "materials_mip%d.png" % (mip)) 437 | 438 | # ____________ Per-scene functions ____________ 439 | 440 | def scenepath(scene): 441 | """Generate path for scene directory""" 442 | return os.path.join(basedir, name(scene)) 443 | 444 | def has_larger_version(scene, mip, hdr): 445 | """Helper that returns whether a larger (lower) mip of a scene is downloaded 446 | """ 447 | return get_larger_version(scene, mip, hdr) != -1 448 | 449 | def get_larger_version(scene, mip, hdr): 450 | """Return index of next-largest miplevel 451 | 452 | Args: 453 | scene: scene name 454 | mip (int): mip that we want to generate 455 | hdr (bool): HDR or not 456 | 457 | Returns: 458 | integer mip level that exists on disk or -1 if no larger mip exists. 459 | """ 460 | for testmip in range(mip-1, -1, -1): 461 | if scene_is_downloaded(scene, testmip, hdr): 462 | return testmip 463 | return -1 464 | 465 | def generate_mipmap(scene, mip, hdr): 466 | """Generate single mip level for one scene. 467 | 468 | Args: 469 | scene: scene name 470 | mip (int): mip that we want to generate 471 | hdr (bool): HDR or not 472 | 473 | Raises: 474 | ValueError: If no larger version of the image exists. 475 | """ 476 | print("generating mipmap %d for scene %s/hdr=%d" % (mip, scene, hdr)) 477 | srcmip = get_larger_version(scene, mip, hdr) 478 | if srcmip == -1: 479 | raise ValueError("Cannot generate mip level %d for scene %s" % (mip, scene)) 480 | 481 | outh, outw = imshape(mip) 482 | for dir in range(25): 483 | I = readimage(impath(scene, dir, srcmip, hdr)) 484 | I = resize_lanczos(I, outh, outw) 485 | writeimage(I, impath(scene, dir, mip, hdr)) 486 | 487 | def generate_probe_size(scenes, *, material, size, base_size, hdr): 488 | """Generate downsampled light probe. 489 | 490 | Requires that the original 256px resolution has already been downloaded. 491 | 492 | Args: 493 | scene: scene name 494 | material: "gray" or "chrome" 495 | size: target size to generate 496 | hdr: HDR or not 497 | """ 498 | if size == base_size: 499 | return 500 | elif size > base_size: 501 | raise ValueError("Can only generate probes that are smaller than 256px") 502 | 503 | print("generating %s probe size %d/hdr=%d for %d scenes" % (material, size, hdr, len(scenes))) 504 | iterator = tqdm.tqdm(scenes) if len(scenes) > 3 else scenes 505 | 506 | for scene in iterator: 507 | for dir in range(25): 508 | I = readimage(probepath(scene, dir, "chrome", base_size, hdr)) 509 | I = resize_lanczos(I, size, size) 510 | writeimage(I, probepath(scene, dir, "chrome", size, hdr)) 511 | 512 | def scene_is_downloaded(scene, mip, hdr): 513 | """Tests whether scene exists on disk as a particular (mip/hdr) version. 514 | 515 | Args: 516 | scene: scene name 517 | mip (int): mip that we want to generate 518 | hdr (bool): HDR or not 519 | 520 | Returns: 521 | bool: True if scene at given mip/hdr exists 522 | """ 523 | testfile = impath(scene, 24, mip, hdr) 524 | return os.path.isfile(testfile) 525 | 526 | def probe_is_downloaded(scene, material, size, hdr): 527 | """Tests whether probe exists on disk as a particular (size/hdr) version. 528 | 529 | Args: 530 | scene: scene name 531 | material: "gray" or "chrome" 532 | size: target size to generate 533 | hdr: HDR or not 534 | 535 | Returns: 536 | bool: True if light probe at given size/hdr exists 537 | """ 538 | testfile = probepath(scene, 24, material, size, hdr) 539 | return os.path.isfile(testfile) 540 | 541 | def material_is_downloaded(scene, mip): 542 | """Tests whether material map exists on disk as mip level. 543 | 544 | Args: 545 | scene: scene name 546 | mip (int): mip that is tested 547 | 548 | Returns: 549 | bool: True if material map at given mip exists 550 | """ 551 | testfile = material_impath(scene, mip=mip) 552 | return os.path.isfile(testfile) 553 | 554 | def download_scenes(scenes=None, *, mip=2, hdr=False, force=False): 555 | """Download and unzip a list of scenes 556 | 557 | Args: 558 | scenes: list of scenes or scene names 559 | mip(int): mip level to download 560 | hdr(bool): whether to download JPG or EXR files 561 | force(bool): force download even if scene already exists on disk. 562 | """ 563 | def download_scene(scene): 564 | 565 | fmt = "exr" if hdr else "jpg" 566 | url = BASE_URL + "/%s/%s_mip%d_%s.zip" % (scene, scene, mip, fmt) 567 | req = urllib.request.urlopen(url) 568 | archive = zipfile.ZipFile(io.BytesIO(req.read())) 569 | archive.extractall(basedir) 570 | 571 | print("Downloading %d scenes" % len(scenes)) 572 | 573 | iterator = tqdm.tqdm(scenes) if len(scenes) > 1 else scenes 574 | for scene in iterator: 575 | scene = name(scene) 576 | 577 | if force or not scene_is_downloaded(scene, mip, hdr): 578 | download_scene(scene) 579 | 580 | def download_probes(scenes, *, material, size, hdr): 581 | """Download and unzip a light probes for list of scenes 582 | 583 | Args: 584 | scenes: list of scenes or scene names 585 | material(string): "gray" or "chrome" 586 | size(int): size in pixels of the requested probe set 587 | hdr(bool): whether to download JPG or EXR files 588 | """ 589 | def download_probe(scene): 590 | fmt = "exr" if hdr else "jpg" 591 | url = BASE_URL + "/%s/%s_probes_%dpx_%s.zip" % (scene, scene, size, fmt) 592 | req = urllib.request.urlopen(url) 593 | archive = zipfile.ZipFile(io.BytesIO(req.read())) 594 | archive.extractall(basedir) 595 | 596 | print("Downloading probes for %d scenes" % len(scenes)) 597 | iterator = tqdm.tqdm(scenes) if len(scenes) > 1 else scenes 598 | 599 | for scene in iterator: 600 | scene = name(scene) 601 | download_probe(scene) 602 | 603 | def download_materials(scenes=None, *, mip): 604 | """Download material map PNG images 605 | 606 | Args: 607 | scenes: list of scenes or scene names 608 | mip(int): mip level to download 609 | """ 610 | 611 | def download_materialmap(scene): 612 | os.makedirs(scenepath(scene), exist_ok=True) 613 | url = BASE_URL + "/%s/materials_mip%d.png" % (scene, mip) 614 | req = urllib.request.urlopen(url) 615 | outfile = open(material_impath(scene, mip), 'wb') 616 | outfile.write(req.read()) 617 | outfile.close() 618 | 619 | print("Downloading %d material maps at mip %d" % (len(scenes), mip)) 620 | iterator = tqdm.tqdm(scenes) if len(scenes) > 1 else scenes 621 | 622 | for scene in iterator: 623 | scene = name(scene) 624 | download_materialmap(scene) 625 | 626 | def ensure_downloaded(scenes, mip, hdr): 627 | """Download scenes (or generate from larger version) if needed 628 | 629 | Args: 630 | scenes: list of scenes or scene names 631 | mip(int): mip level to download 632 | hdr(bool): whether to download JPG or EXR files 633 | """ 634 | if not isinstance(scenes, Iterable): 635 | scenes = [scenes] 636 | 637 | not_downloaded = [] 638 | 639 | for scene in scenes: 640 | if not scene_is_downloaded(scene, mip, hdr): 641 | if has_larger_version(scene, mip, hdr): 642 | generate_mipmap(scene, mip, hdr) 643 | else: 644 | not_downloaded.append(scene) 645 | if not_downloaded: 646 | download_scenes(not_downloaded, mip=mip, hdr=hdr) 647 | 648 | def ensure_probes_downloaded(scenes, *, material, size, hdr): 649 | """Download light probes (or generate from larger version) if needed 650 | 651 | Args: 652 | scenes: list of scenes or scene names 653 | material(string): "gray" or "chrome" 654 | size(int): size in pixels of the requested probe set 655 | hdr(bool): whether to download JPG or EXR files 656 | """ 657 | 658 | must_download = [] 659 | must_generate = [] 660 | for scene in scenes: 661 | probe_loaded = probe_is_downloaded(scene, material, size, hdr) 662 | baseprobe_loaded = probe_is_downloaded(scene, material, 256, hdr) 663 | 664 | if not probe_loaded: 665 | must_generate.append(scene) 666 | 667 | if not probe_loaded and not baseprobe_loaded: 668 | must_download.append(scene) 669 | 670 | if must_download: 671 | download_probes(must_download, material=material, size=256, hdr=hdr) 672 | 673 | if must_generate: 674 | generate_probe_size(scenes, material=material, size=size, base_size=256, hdr=hdr) 675 | 676 | def ensure_materials_downloaded(scenes, *, mip): 677 | """Download material maps if needed 678 | 679 | Args: 680 | scenes: list of scenes or scene names 681 | mip(int): mip level to download 682 | """ 683 | 684 | not_loaded = [] 685 | 686 | for scene in scenes: 687 | if not material_is_downloaded(scene, mip): 688 | not_loaded.append(scene) 689 | 690 | if not_loaded: 691 | download_materials(not_loaded, mip=mip) 692 | 693 | def ensure_checkpoint_downloaded(cp): 694 | if os.path.isfile(cp): 695 | return 696 | 697 | os.makedirs(os.path.dirname(cp), exist_ok=True) 698 | url = "https://data.csail.mit.edu/multilum/%s" % cp 699 | print("Download model checkpoint from %s" % url) 700 | req = urllib.request.urlopen(url) 701 | outfile = open(cp, 'wb') 702 | outfile.write(req.read()) 703 | outfile.close() 704 | 705 | # ____________ Utility functions ____________ 706 | def name(obj_or_name): 707 | if isinstance(obj_or_name, str): 708 | return obj_or_name 709 | else: 710 | return obj_or_name.name 711 | 712 | def id(obj_or_id): 713 | if isinstance(obj_or_id, int): 714 | return obj_or_id 715 | else: 716 | return obj_or_id.id 717 | 718 | def dtype(hdr): 719 | return 'float32' if hdr else 'uint8' 720 | 721 | def ext(hdr): 722 | return "exr" if hdr else "jpg" 723 | 724 | def sanitize_scenes_arg(scenes): 725 | if scenes is None: 726 | scenes = all_scenes() 727 | elif isinstance(scenes, (str, Scene)): 728 | scenes = [scenes] 729 | return scenes 730 | 731 | def sanitize_room_types_arg(room_types): 732 | if room_types is None: 733 | room_types = all_room_types() 734 | elif isinstance(room_types, (str, RoomType)): 735 | room_types = [room_types] 736 | return room_types 737 | 738 | def sanitize_buildings_arg(buildings): 739 | if buildings is None: 740 | buildings = all_buildings() 741 | elif isinstance(buildings, (str, Building)): 742 | buildings = [buildings] 743 | return buildings 744 | 745 | def sanitize_rooms_arg_to_ids(rooms): 746 | if rooms is None: 747 | rooms = all_rooms() 748 | elif isinstance(rooms, (str, Room)): 749 | rooms = [rooms] 750 | 751 | ret = [] 752 | for room in rooms: 753 | if isinstance(room, Room): 754 | room = "%s/%s" % (room.building.name, room.name) 755 | ret.append(room) 756 | return ret 757 | 758 | def sanitize_dirs_arg(dirs): 759 | if dirs is None: 760 | dirs = list(range(25)) 761 | elif isinstance(dirs, int): 762 | dirs = [dirs] 763 | return dirs 764 | 765 | 766 | # ____________ MAIN FUNCTION --- Example Usage ____________ 767 | 768 | 769 | def demo_multi_illumination(): 770 | from matplotlib import pyplot as plt 771 | 772 | print("=== Multi-Illumination Image Demo ===") 773 | scenes = ['main_experiment120', 'kingston_dining10', 'elm_revis_kitchen14'] 774 | I = query_images(scenes, mip=4) 775 | 776 | dir1 = 14 777 | dir2 = 24 778 | 779 | for i in range(3): 780 | plt.subplot(2,3,i+1) 781 | plt.imshow(I[i,dir1]) 782 | plt.subplot(2,3,i+4) 783 | plt.imshow(I[i,dir2]) 784 | 785 | plt.show() 786 | 787 | def demo_light_probes(): 788 | from matplotlib import pyplot as plt 789 | scenes = ['main_experiment120', 'kingston_dining10', 'elm_revis_kitchen14'] 790 | 791 | print("=== Light Probe Demo ===") 792 | P = query_probes(scenes) 793 | 794 | for i in range(25): 795 | plt.subplot(5,5,i+1) 796 | plt.imshow(P[0,i]) 797 | plt.show() 798 | 799 | def demo_materials(): 800 | from matplotlib import pyplot as plt 801 | 802 | print("=== Material Demo ===") 803 | M = query_materials('main_experiment120', mip=4) 804 | print(M.shape) 805 | plt.subplot(121) 806 | plt.imshow(M[0]) 807 | 808 | M = query_materials('main_experiment120', mip=4, apply_colors=True) 809 | print(M.shape) 810 | plt.subplot(122) 811 | plt.imshow(M[0]) 812 | plt.show() 813 | 814 | def demo_meta_data(): 815 | print("Scenes") 816 | scenes = all_scenes() 817 | for s in scenes[:3]: 818 | print(s) 819 | print("... total %d scenes\n" % len(scenes)) 820 | 821 | print("Buildings") 822 | buildings = all_buildings() 823 | for loc in buildings[:3]: 824 | print(loc) 825 | print("... total %d buildings\n" % len(buildings)) 826 | 827 | print("Rooms") 828 | rooms = all_rooms() 829 | for r in rooms[:3]: 830 | print(r) 831 | print("... total %d rooms\n" % len(rooms)) 832 | 833 | print("Room Types") 834 | room_types = all_room_types() 835 | for rt in room_types[:3]: 836 | print(rt) 837 | print("... total %d room types\n" % len(room_types)) 838 | 839 | def main(): 840 | demo_multi_illumination() 841 | demo_light_probes() 842 | demo_materials() 843 | demo_meta_data() 844 | 845 | if __name__ == "__main__": 846 | main() 847 | -------------------------------------------------------------------------------- /probe_predict/eval.py: -------------------------------------------------------------------------------- 1 | """Evaluation code for illumination estimation task. 2 | 3 | Usage example: 4 | 5 | scene=everett_kitchen7 6 | mkdir -p eval_output/probe_predict 7 | for light_dir in `seq 6 7`; do 8 | python3 -m probe_predict.eval \ 9 | --light_dir ${light_dir} \ 10 | --out eval_output/probe_predict/${scene}_dir${light_dir}.jpg \ 11 | ${scene} 12 | done 13 | 14 | Last evaluated on PyTorch 1.0.0.dev20181024 15 | """ 16 | 17 | import argparse 18 | import os 19 | import urllib 20 | 21 | from probe_predict import model 22 | 23 | import numpy as np 24 | import torch as th 25 | 26 | import multilum 27 | 28 | def main(): 29 | parser = argparse.ArgumentParser() 30 | 31 | parser.add_argument('scene') 32 | parser.add_argument('--light_dir', type=int) 33 | parser.add_argument('-o', '--out') 34 | 35 | parser.add_argument("--cropx", type=int, default=512) 36 | parser.add_argument("--cropy", type=int, default=256) 37 | 38 | parser.add_argument("--cropsz", type=int, default=512) 39 | parser.add_argument("--checkpoint", required=False) 40 | parser.add_argument("--mip", type=int, default=2) 41 | parser.add_argument("--chromesz", type=int, default=64) 42 | parser.add_argument("--fmaps1", type=int, default=6) 43 | parser.add_argument("--gpu_id", type=int, default=0) 44 | opts = parser.parse_args() 45 | 46 | if opts.checkpoint is None: 47 | opts.checkpoint = \ 48 | "checkpoints/probe_predict/t_1547304767_nsteps_000050000.checkpoint" 49 | multilum.ensure_checkpoint_downloaded(opts.checkpoint) 50 | 51 | model = SingleImageEvaluator(opts) 52 | 53 | I = multilum.query_images(opts.scene, mip=opts.mip, 54 | dirs=opts.light_dir)[0,0] 55 | 56 | # extract crop 57 | ox = opts.cropx 58 | oy = opts.cropy 59 | cropnp = I[oy:oy+opts.cropsz, ox:ox+opts.cropsz] 60 | 61 | # pre process input 62 | crop = np.moveaxis(cropnp, 2, 0) 63 | crop = crop / 255 - 0.5 64 | 65 | # run model 66 | pred = model.forward(crop) 67 | 68 | # post process prediction 69 | pred = np.moveaxis(pred, 0, 2) + 0.5 70 | pred = (np.clip(autoexpose(pred), 0, 1) * 255).astype('uint8') 71 | 72 | # write results 73 | multilum.writejpg(cropnp, "%s.input.jpg" % opts.out) 74 | multilum.writejpg(pred, "%s.pred.jpg" % opts.out) 75 | 76 | 77 | 78 | 79 | def autoexpose(I): 80 | n = np.percentile(I[:,:,1], 90) 81 | if n > 0: 82 | I = I / n 83 | return I 84 | 85 | class SingleImageEvaluator: 86 | def __init__(self, opts): 87 | self.net = model.make_net_fully_convolutional( 88 | chromesz=opts.chromesz, 89 | fmaps1=opts.fmaps1, 90 | xysize=opts.cropsz) 91 | self.net.cuda() 92 | self.net.load_state_dict(th.load(opts.checkpoint)) 93 | 94 | def forward(self, image): 95 | """Accept (3,512,512) image in [-0.5; 0.5] range. 96 | 97 | Returns (3,64,64) image in [-0.5; 0.5] range. 98 | """ 99 | x = image 100 | h, w = image.shape[-2:] 101 | 102 | x = th.Tensor(x).view(1, 3, h, w) 103 | self.net.zero_grad() 104 | with th.no_grad(): 105 | pred = self.net(th.autograd.Variable(x).cuda()) 106 | 107 | pred = pred.view(3, self.net.chromesz, self.net.chromesz) 108 | pred = pred.detach().cpu().numpy() 109 | return pred 110 | 111 | 112 | if __name__ == "__main__": 113 | main() -------------------------------------------------------------------------------- /probe_predict/model.py: -------------------------------------------------------------------------------- 1 | """Pytorch model for illumination estimation task.""" 2 | 3 | from torch import nn 4 | import math 5 | 6 | 7 | def make_encoder(fmaps, fmaps1=None, xysize=None): 8 | if fmaps1 is None: 9 | fmaps1 = fmaps 10 | layers = [] 11 | 12 | ndowns = int(math.log2(xysize)) 13 | for i in range(ndowns): 14 | if i == 0: 15 | fmaps_out = fmaps1 16 | else: 17 | fmaps_out = fmaps * 2 18 | 19 | layers.extend([ 20 | nn.Conv2d(fmaps, fmaps_out, 3, padding=1, bias=True), 21 | nn.ReLU(), 22 | nn.MaxPool2d(2) 23 | ]) 24 | fmaps = fmaps_out 25 | xysize //= 2 26 | print("%d layer network downsample to %dx%dx%d" % (ndowns, fmaps, xysize, xysize)) 27 | return layers, fmaps, xysize 28 | 29 | def make_net_fully_convolutional(*,chromesz, fmaps1, xysize): 30 | layers, fmaps, _ = make_encoder(fmaps=3, fmaps1=fmaps1, xysize=xysize) 31 | fmaps_out = chromesz*chromesz*3 32 | layers.append(nn.Conv2d(fmaps, fmaps_out, 1, padding=0, bias=True)) 33 | 34 | ret = nn.Sequential(*layers) 35 | ret.fully_convolutional = True 36 | ret.chromesz = chromesz 37 | ret.fmaps1 = fmaps1 38 | ret.cropsz = xysize 39 | return ret 40 | -------------------------------------------------------------------------------- /probepred_eval.sh: -------------------------------------------------------------------------------- 1 | # Runs on PyTorch 1.3.1 2 | set -e 3 | export PYTHONPATH=. 4 | 5 | scene=everett_kitchen7 6 | 7 | mkdir -p eval_output/probe_predict 8 | for light_dir in `seq 6 7`; do 9 | 10 | python3 -m probe_predict.eval \ 11 | --light_dir ${light_dir} \ 12 | --out eval_output/probe_predict/${scene}_dir${light_dir}.jpg \ 13 | ${scene} 14 | done 15 | 16 | -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | 4 | # A comma-separated list of package or module names from where C extensions may 5 | # be loaded. Extensions are loading into the active Python interpreter and may 6 | # run arbitrary code. 7 | extension-pkg-whitelist=OpenEXR 8 | 9 | # Add files or directories to the blacklist. They should be base names, not 10 | # paths. 11 | ignore=CVS 12 | 13 | # Add files or directories matching the regex patterns to the blacklist. The 14 | # regex matches against base names, not paths. 15 | ignore-patterns= 16 | 17 | # Python code to execute, usually for sys.path manipulation such as 18 | # pygtk.require(). 19 | #init-hook= 20 | 21 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the 22 | # number of processors available to use. 23 | jobs=1 24 | 25 | # Control the amount of potential inferred values when inferring a single 26 | # object. This can help the performance when dealing with large functions or 27 | # complex, nested conditions. 28 | limit-inference-results=100 29 | 30 | # List of plugins (as comma separated values of python modules names) to load, 31 | # usually to register additional checkers. 32 | load-plugins= 33 | 34 | # Pickle collected data for later comparisons. 35 | persistent=yes 36 | 37 | # Specify a configuration file. 38 | #rcfile= 39 | 40 | # When enabled, pylint would attempt to guess common misconfiguration and emit 41 | # user-friendly hints instead of false-positive error messages. 42 | suggestion-mode=yes 43 | 44 | # Allow loading of arbitrary C extensions. Extensions are imported into the 45 | # active Python interpreter and may run arbitrary code. 46 | unsafe-load-any-extension=no 47 | 48 | 49 | [MESSAGES CONTROL] 50 | 51 | # Only show warnings with the listed confidence levels. Leave empty to show 52 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. 53 | confidence= 54 | 55 | # Disable the message, report, category or checker with the given id(s). You 56 | # can either give multiple identifiers separated by comma (,) or put this 57 | # option multiple times (only on the command line, not in the configuration 58 | # file where it should appear only once). You can also use "--disable=all" to 59 | # disable everything first and then reenable specific checks. For example, if 60 | # you want to run only the similarities checker, you can use "--disable=all 61 | # --enable=similarities". If you want to run only the classes checker, but have 62 | # no Warning level messages displayed, use "--disable=all --enable=classes 63 | # --disable=W". 64 | disable=parameter-unpacking, 65 | unpacking-in-except, 66 | backtick, 67 | long-suffix, 68 | old-octal-literal, 69 | import-star-module-level, 70 | raw-checker-failed, 71 | bad-inline-option, 72 | locally-disabled, 73 | file-ignored, 74 | suppressed-message, 75 | useless-suppression, 76 | deprecated-pragma, 77 | use-symbolic-message-instead, 78 | coerce-method, 79 | delslice-method, 80 | getslice-method, 81 | setslice-method, 82 | old-division, 83 | dict-iter-method, 84 | dict-view-method, 85 | next-method-called, 86 | metaclass-assignment, 87 | indexing-exception, 88 | reload-builtin, 89 | oct-method, 90 | hex-method, 91 | nonzero-method, 92 | cmp-method, 93 | using-cmp-argument, 94 | exception-message-attribute, 95 | invalid-str-codec, 96 | sys-max-int, 97 | too-few-public-methods, 98 | invalid-name, 99 | bad-whitespace, 100 | bad-continuation, 101 | fixme, 102 | bad-indentation, 103 | no-else-return, 104 | no-self-use, 105 | # bad-indentation, 106 | # missing-docstring, 107 | # no-member, 108 | # unnecessary-lambda, 109 | # arguments-differ, 110 | global-statement, 111 | # unused-variable, 112 | redefined-builtin, 113 | import-outside-toplevel, 114 | 115 | # Enable the message, report, category or checker with the given id(s). You can 116 | # either give multiple identifier separated by comma (,) or put this option 117 | # multiple time (only on the command line, not in the configuration file where 118 | # it should appear only once). See also the "--disable" option for examples. 119 | enable=c-extension-no-member 120 | 121 | 122 | [REPORTS] 123 | 124 | # Python expression which should return a note less than 10 (10 is the highest 125 | # note). You have access to the variables errors warning, statement which 126 | # respectively contain the number of errors / warnings messages and the total 127 | # number of statements analyzed. This is used by the global evaluation report 128 | # (RP0004). 129 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 130 | 131 | # Template used to display messages. This is a python new-style format string 132 | # used to format the message information. See doc for all details. 133 | #msg-template= 134 | 135 | # Set the output format. Available formats are text, parseable, colorized, json 136 | # and msvs (visual studio). You can also give a reporter class, e.g. 137 | # mypackage.mymodule.MyReporterClass. 138 | output-format=text 139 | 140 | # Tells whether to display a full report or only the messages. 141 | reports=no 142 | 143 | # Activate the evaluation score. 144 | score=no 145 | 146 | 147 | [REFACTORING] 148 | 149 | # Maximum number of nested blocks for function / method body 150 | max-nested-blocks=5 151 | 152 | # Complete name of functions that never returns. When checking for 153 | # inconsistent-return-statements if a never returning function is called then 154 | # it will be considered as an explicit return statement and no message will be 155 | # printed. 156 | never-returning-functions=sys.exit 157 | 158 | 159 | [BASIC] 160 | 161 | # Naming style matching correct argument names. 162 | argument-naming-style=snake_case 163 | 164 | # Regular expression matching correct argument names. Overrides argument- 165 | # naming-style. 166 | #argument-rgx= 167 | 168 | # Naming style matching correct attribute names. 169 | attr-naming-style=snake_case 170 | 171 | # Regular expression matching correct attribute names. Overrides attr-naming- 172 | # style. 173 | #attr-rgx= 174 | 175 | # Bad variable names which should always be refused, separated by a comma. 176 | bad-names=foo, 177 | bar, 178 | baz, 179 | toto, 180 | tutu, 181 | tata 182 | 183 | # Naming style matching correct class attribute names. 184 | class-attribute-naming-style=any 185 | 186 | # Regular expression matching correct class attribute names. Overrides class- 187 | # attribute-naming-style. 188 | #class-attribute-rgx= 189 | 190 | # Naming style matching correct class names. 191 | class-naming-style=PascalCase 192 | 193 | # Regular expression matching correct class names. Overrides class-naming- 194 | # style. 195 | #class-rgx= 196 | 197 | # Naming style matching correct constant names. 198 | const-naming-style=UPPER_CASE 199 | 200 | # Regular expression matching correct constant names. Overrides const-naming- 201 | # style. 202 | #const-rgx= 203 | 204 | # Minimum line length for functions/classes that require docstrings, shorter 205 | # ones are exempt. 206 | docstring-min-length=-1 207 | 208 | # Naming style matching correct function names. 209 | function-naming-style=snake_case 210 | 211 | # Regular expression matching correct function names. Overrides function- 212 | # naming-style. 213 | #function-rgx= 214 | 215 | # Good variable names which should always be accepted, separated by a comma. 216 | good-names=i, 217 | j, 218 | k, 219 | ex, 220 | Run, 221 | _ 222 | 223 | # Include a hint for the correct naming format with invalid-name. 224 | include-naming-hint=no 225 | 226 | # Naming style matching correct inline iteration names. 227 | inlinevar-naming-style=any 228 | 229 | # Regular expression matching correct inline iteration names. Overrides 230 | # inlinevar-naming-style. 231 | #inlinevar-rgx= 232 | 233 | # Naming style matching correct method names. 234 | method-naming-style=snake_case 235 | 236 | # Regular expression matching correct method names. Overrides method-naming- 237 | # style. 238 | #method-rgx= 239 | 240 | # Naming style matching correct module names. 241 | module-naming-style=snake_case 242 | 243 | # Regular expression matching correct module names. Overrides module-naming- 244 | # style. 245 | #module-rgx= 246 | 247 | # Colon-delimited sets of names that determine each other's naming style when 248 | # the name regexes allow several styles. 249 | name-group= 250 | 251 | # Regular expression which should only match function or class names that do 252 | # not require a docstring. 253 | no-docstring-rgx=^_ 254 | 255 | # List of decorators that produce properties, such as abc.abstractproperty. Add 256 | # to this list to register other decorators that produce valid properties. 257 | # These decorators are taken in consideration only for invalid-name. 258 | property-classes=abc.abstractproperty 259 | 260 | # Naming style matching correct variable names. 261 | variable-naming-style=snake_case 262 | 263 | # Regular expression matching correct variable names. Overrides variable- 264 | # naming-style. 265 | #variable-rgx= 266 | 267 | 268 | [MISCELLANEOUS] 269 | 270 | # List of note tags to take in consideration, separated by a comma. 271 | notes=FIXME, 272 | XXX, 273 | TODO 274 | 275 | 276 | [SPELLING] 277 | 278 | # Limits count of emitted suggestions for spelling mistakes. 279 | max-spelling-suggestions=4 280 | 281 | # Spelling dictionary name. Available dictionaries: none. To make it working 282 | # install python-enchant package.. 283 | spelling-dict= 284 | 285 | # List of comma separated words that should not be checked. 286 | spelling-ignore-words= 287 | 288 | # A path to a file that contains private dictionary; one word per line. 289 | spelling-private-dict-file= 290 | 291 | # Tells whether to store unknown words to indicated private dictionary in 292 | # --spelling-private-dict-file option instead of raising a message. 293 | spelling-store-unknown-words=no 294 | 295 | 296 | [SIMILARITIES] 297 | 298 | # Ignore comments when computing similarities. 299 | ignore-comments=yes 300 | 301 | # Ignore docstrings when computing similarities. 302 | ignore-docstrings=yes 303 | 304 | # Ignore imports when computing similarities. 305 | ignore-imports=no 306 | 307 | # Minimum lines number of a similarity. 308 | min-similarity-lines=4 309 | 310 | 311 | [LOGGING] 312 | 313 | # Format style used to check logging format string 314 | # logging-format-style=% 315 | 316 | # Logging modules to check that the string format arguments are in logging 317 | # function parameter format. 318 | logging-modules=logging 319 | 320 | 321 | [VARIABLES] 322 | 323 | # List of additional names supposed to be defined in builtins. Remember that 324 | # you should avoid defining new builtins when possible. 325 | additional-builtins= 326 | 327 | # Tells whether unused global variables should be treated as a violation. 328 | allow-global-unused-variables=yes 329 | 330 | # List of strings which can identify a callback function by name. A callback 331 | # name must start or end with one of those strings. 332 | callbacks=cb_, 333 | _cb 334 | 335 | # A regular expression matching the name of dummy variables (i.e. expected to 336 | # not be used). 337 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 338 | 339 | # Argument names that match this expression will be ignored. Default to name 340 | # with leading underscore. 341 | ignored-argument-names=_.*|^ignored_|^unused_ 342 | 343 | # Tells whether we should check for unused import in __init__ files. 344 | init-import=no 345 | 346 | # List of qualified module names which can have objects that can redefine 347 | # builtins. 348 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io 349 | 350 | 351 | [TYPECHECK] 352 | 353 | # List of decorators that produce context managers, such as 354 | # contextlib.contextmanager. Add to this list to register other decorators that 355 | # produce valid context managers. 356 | contextmanager-decorators=contextlib.contextmanager 357 | 358 | # List of members which are set dynamically and missed by pylint inference 359 | # system, and so shouldn't trigger E1101 when accessed. Python regular 360 | # expressions are accepted. 361 | generated-members= 362 | 363 | # Tells whether missing members accessed in mixin class should be ignored. A 364 | # mixin class is detected if its name ends with "mixin" (case insensitive). 365 | ignore-mixin-members=yes 366 | 367 | # Tells whether to warn about missing members when the owner of the attribute 368 | # is inferred to be None. 369 | ignore-none=yes 370 | 371 | # This flag controls whether pylint should warn about no-member and similar 372 | # checks whenever an opaque object is returned when inferring. The inference 373 | # can return multiple potential results while evaluating a Python object, but 374 | # some branches might not be evaluated, which results in partial inference. In 375 | # that case, it might be useful to still emit no-member and other checks for 376 | # the rest of the inferred objects. 377 | ignore-on-opaque-inference=yes 378 | 379 | # List of class names for which member attributes should not be checked (useful 380 | # for classes with dynamically set attributes). This supports the use of 381 | # qualified names. 382 | ignored-classes=optparse.Values,thread._local,_thread._local 383 | 384 | # List of module names for which member attributes should not be checked 385 | # (useful for modules/projects where namespaces are manipulated during runtime 386 | # and thus existing member attributes cannot be deduced by static analysis. It 387 | # supports qualified module names, as well as Unix pattern matching. 388 | ignored-modules= 389 | 390 | # Show a hint with possible names when a member name was not found. The aspect 391 | # of finding the hint is based on edit distance. 392 | missing-member-hint=yes 393 | 394 | # The minimum edit distance a name should have in order to be considered a 395 | # similar match for a missing member name. 396 | missing-member-hint-distance=1 397 | 398 | # The total number of similar names that should be taken in consideration when 399 | # showing a hint for a missing member. 400 | missing-member-max-choices=1 401 | 402 | 403 | [FORMAT] 404 | 405 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 406 | expected-line-ending-format= 407 | 408 | # Regexp for a line that is allowed to be longer than the limit. 409 | ignore-long-lines=^\s*(# )??$ 410 | 411 | # Number of spaces of indent required inside a hanging or continued line. 412 | indent-after-paren=2 413 | 414 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 415 | # tab). 416 | indent-string=' ' 417 | 418 | # Maximum number of characters on a single line. 419 | max-line-length=100 420 | 421 | # Maximum number of lines in a module. 422 | max-module-lines=1000 423 | 424 | # List of optional constructs for which whitespace checking is disabled. `dict- 425 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. 426 | # `trailing-comma` allows a space between comma and closing bracket: (a, ). 427 | # `empty-line` allows space-only lines. 428 | no-space-check=trailing-comma, 429 | dict-separator 430 | 431 | # Allow the body of a class to be on the same line as the declaration if body 432 | # contains single statement. 433 | single-line-class-stmt=no 434 | 435 | # Allow the body of an if to be on the same line as the test if there is no 436 | # else. 437 | single-line-if-stmt=no 438 | 439 | 440 | [CLASSES] 441 | 442 | # List of method names used to declare (i.e. assign) instance attributes. 443 | defining-attr-methods=__init__, 444 | __new__, 445 | setUp 446 | 447 | # List of member names, which should be excluded from the protected access 448 | # warning. 449 | exclude-protected=_asdict, 450 | _fields, 451 | _replace, 452 | _source, 453 | _make 454 | 455 | # List of valid names for the first argument in a class method. 456 | valid-classmethod-first-arg=cls 457 | 458 | # List of valid names for the first argument in a metaclass class method. 459 | valid-metaclass-classmethod-first-arg=cls 460 | 461 | 462 | [IMPORTS] 463 | 464 | # Allow wildcard imports from modules that define __all__. 465 | allow-wildcard-with-all=no 466 | 467 | # Analyse import fallback blocks. This can be used to support both Python 2 and 468 | # 3 compatible code, which means that the block might have code that exists 469 | # only in one or another interpreter, leading to false positives when analysed. 470 | analyse-fallback-blocks=no 471 | 472 | # Deprecated modules which should not be used, separated by a comma. 473 | deprecated-modules=optparse,tkinter.tix 474 | 475 | # Create a graph of external dependencies in the given file (report RP0402 must 476 | # not be disabled). 477 | ext-import-graph= 478 | 479 | # Create a graph of every (i.e. internal and external) dependencies in the 480 | # given file (report RP0402 must not be disabled). 481 | import-graph= 482 | 483 | # Create a graph of internal dependencies in the given file (report RP0402 must 484 | # not be disabled). 485 | int-import-graph= 486 | 487 | # Force import order to recognize a module as part of the standard 488 | # compatibility libraries. 489 | known-standard-library= 490 | 491 | # Force import order to recognize a module as part of a third party library. 492 | known-third-party=enchant 493 | 494 | 495 | [DESIGN] 496 | 497 | # Maximum number of arguments for function / method. 498 | max-args=5 499 | 500 | # Maximum number of attributes for a class (see R0902). 501 | max-attributes=7 502 | 503 | # Maximum number of boolean expressions in an if statement. 504 | max-bool-expr=5 505 | 506 | # Maximum number of branch for function / method body. 507 | max-branches=12 508 | 509 | # Maximum number of locals for function / method body. 510 | max-locals=15 511 | 512 | # Maximum number of parents for a class (see R0901). 513 | max-parents=7 514 | 515 | # Maximum number of public methods for a class (see R0904). 516 | max-public-methods=20 517 | 518 | # Maximum number of return / yield for function / method body. 519 | max-returns=6 520 | 521 | # Maximum number of statements in function / method body. 522 | max-statements=50 523 | 524 | # Minimum number of public methods for a class (see R0903). 525 | min-public-methods=2 526 | 527 | 528 | [EXCEPTIONS] 529 | 530 | # Exceptions that will emit a warning when being caught. Defaults to 531 | # "Exception". 532 | overgeneral-exceptions=Exception 533 | -------------------------------------------------------------------------------- /relight/eval.py: -------------------------------------------------------------------------------- 1 | """Evaluate relighting model.""" 2 | 3 | import os 4 | import argparse 5 | 6 | import numpy as np 7 | import torch as th 8 | 9 | import multilum 10 | from relight.model import Relighter 11 | 12 | 13 | def main(opts): 14 | 15 | # The model is trained to expect input illumination 0 (light form behind the camera) 16 | src = [0] 17 | # In a single forward pass, we predict the scene under 8 novel light conditions. 18 | tgt = [5, 7, 12, 4, 16, 6, 17, 11] 19 | num_lights = len(tgt) 20 | 21 | model = Relighter(**{ 22 | 'n_in': 1, 23 | 'n_out': 8, 24 | 'normals': False}) 25 | model.cuda() 26 | 27 | multilum.ensure_checkpoint_downloaded(opts.checkpoint) 28 | chkpt = th.load(opts.checkpoint) 29 | model.load_state_dict(chkpt["model"]) 30 | 31 | eps = 1e-4 32 | 33 | sample = { 34 | "input": _preprocess(multilum.query_images(opts.scene, dirs=src, mip=3, hdr=True)[0]), 35 | "target": _preprocess(multilum.query_images(opts.scene, dirs=tgt, mip=3, hdr=True)[0]) 36 | } 37 | with th.no_grad(): 38 | 39 | in_ = sample["input"] 40 | in_ = th.log(eps + in_) 41 | mean = in_.mean() 42 | # in_ -= mean 43 | # in_ -= 1 44 | # in_ = th.exp(in_) - eps 45 | 46 | out = model.forward(sample) 47 | # undo normalization 48 | out = th.log(eps + out) 49 | out += 1 50 | out += mean 51 | out = th.exp(out) - eps 52 | 53 | # From here on on it is just gamma correction and file output 54 | in_ = sample["input"].detach() 55 | gt = sample["target"].detach() 56 | 57 | # gamma correction 58 | gamma = 1.0/2.2 59 | in_ = th.pow(in_, gamma) 60 | gt = th.pow(gt, gamma) 61 | out = th.pow(out, gamma) 62 | 63 | in_ = th.clamp(in_, 0, 1) 64 | gt = th.clamp(gt, 0, 1) 65 | recons = th.clamp(out.detach(), 0, 1) 66 | 67 | # write input file 68 | fname_in = os.path.join(opts.output, "input", "%s_dir%d.jpg" % (opts.scene, src[0])) 69 | _save(in_, fname_in) 70 | 71 | bs, _, h, w = recons.shape 72 | recons = recons.view(bs, num_lights, 3, h, w) 73 | gt = gt.view(bs, num_lights, 3, h, w) 74 | 75 | # write predictions and ground truth 76 | for l_idx in range(num_lights): 77 | _sname = "%s_dir%d.jpg" % (opts.scene, tgt[l_idx]) 78 | r = recons[:, l_idx] 79 | g = gt[:, l_idx] 80 | fname_recons = os.path.join(opts.output, "relight", _sname) 81 | _save(r, fname_recons) 82 | fname_gt = os.path.join(opts.output, "gt", _sname) 83 | _save(g, fname_gt) 84 | print("write result", fname_recons) 85 | 86 | print("write input", fname_in) 87 | 88 | 89 | def _preprocess(I): 90 | I = I.copy() 91 | I[I<0] = 0 92 | I = I / np.percentile(I, 90) 93 | return th.Tensor(np.moveaxis(I, 3, 1)).cuda() 94 | 95 | def _save(data, fname, makedirs=True): 96 | if makedirs: 97 | os.makedirs(os.path.dirname(fname), exist_ok=True) 98 | npdata = data[0].cpu().permute(1, 2, 0).numpy() 99 | npdata = (np.clip(npdata, 0, 1) * 255).astype('uint8') 100 | multilum.writeimage(npdata, fname) 101 | 102 | 103 | 104 | if __name__ == "__main__": 105 | parser = argparse.ArgumentParser() 106 | parser.add_argument('--checkpoint') 107 | parser.add_argument('--output') 108 | parser.add_argument('--scene', default="everett_kitchen7") 109 | opts = parser.parse_args() 110 | main(opts) 111 | -------------------------------------------------------------------------------- /relight/model.py: -------------------------------------------------------------------------------- 1 | """Reusable modules for relighting model.""" 2 | 3 | import torch as th 4 | import torch.nn as nn 5 | import torch.nn.functional as F 6 | 7 | ######################################### 8 | 9 | 10 | 11 | 12 | class Relighter(nn.Module): 13 | """ 14 | 2018-04-08 15 | """ 16 | def __init__(self, width=64, n_in=1, n_out=1, normals=False): 17 | super(Relighter, self).__init__() 18 | 19 | self.normals = normals 20 | 21 | w = width 22 | 23 | n_in = 3*n_in 24 | n_out = 3*n_out 25 | 26 | if normals: 27 | n_in += 3 28 | 29 | self.autoencoder = Autoencoder( 30 | n_in, n_out, num_levels=8, 31 | increase_factor=2, 32 | num_convs=2, width=w, ksize=3, 33 | activation="relu", 34 | pooling="max", 35 | output_type="linear") 36 | 37 | def forward(self, samples): 38 | im = samples["input"] 39 | if "normals" in samples.keys() and self.normals: 40 | im = th.cat([im, samples["normals"]], -3) 41 | out = self.autoencoder(im) 42 | return out 43 | 44 | 45 | class ConvChain(nn.Module): 46 | def __init__(self, ninputs, noutputs, ksize=3, width=64, depth=3, stride=1, 47 | pad=True, normalize=False, normalization_type="batch", 48 | output_type="linear", 49 | activation="relu", weight_norm=True): 50 | super(ConvChain, self).__init__() 51 | 52 | assert depth > 0 53 | 54 | if pad: 55 | padding = ksize//2 56 | else: 57 | padding = 0 58 | 59 | layers = [] 60 | for d in range(depth-1): 61 | if d == 0: 62 | _in = ninputs 63 | else: 64 | _in = width 65 | layers.append( 66 | ConvBNRelu( 67 | _in, ksize, width, normalize=normalize, normalization_type="batch", padding=padding, 68 | stride=stride, activation=activation, weight_norm=weight_norm)) 69 | 70 | # Last layer 71 | if depth > 1: 72 | _in = width 73 | else: 74 | _in = ninputs 75 | 76 | conv = nn.Conv2d(_in, noutputs, ksize, bias=True, padding=padding) 77 | if weight_norm: 78 | conv = nn.utils.weight_norm(conv) # TODO 79 | conv.bias.data.zero_() 80 | if output_type == "elu" or output_type == "softplus": 81 | nn.init.xavier_uniform_( 82 | conv.weight.data, nn.init.calculate_gain("relu")) 83 | else: 84 | nn.init.xavier_uniform_( 85 | conv.weight.data, nn.init.calculate_gain(output_type)) 86 | layers.append(conv) 87 | 88 | # Rename layers 89 | for im, m in enumerate(layers): 90 | if im == len(layers)-1: 91 | name = "prediction" 92 | else: 93 | name = "layer_{}".format(im) 94 | self.add_module(name, m) 95 | 96 | if output_type == "linear": 97 | pass 98 | elif output_type == "relu": 99 | self.add_module("output_activation", nn.ReLU(inplace=True)) 100 | elif output_type == "leaky_relu": 101 | self.add_module("output_activation", nn.LeakyReLU(inplace=True)) 102 | elif output_type == "sigmoid": 103 | self.add_module("output_activation", nn.Sigmoid()) 104 | elif output_type == "tanh": 105 | self.add_module("output_activation", nn.Tanh()) 106 | elif output_type == "elu": 107 | self.add_module("output_activation", nn.ELU()) 108 | elif output_type == "softplus": 109 | self.add_module("output_activation", nn.Softplus()) 110 | else: 111 | raise ValueError("Unknon output type '{}'".format(output_type)) 112 | 113 | def forward(self, x): 114 | for m in self.children(): 115 | x = m(x) 116 | return x 117 | 118 | 119 | class ConvBNRelu(nn.Module): 120 | def __init__(self, ninputs, ksize, noutputs, normalize=False, 121 | normalization_type="batch", stride=1, padding=0, 122 | activation="relu", weight_norm=True): 123 | super(ConvBNRelu, self).__init__() 124 | if activation == "relu": 125 | act_fn = nn.ReLU 126 | elif activation == "leaky_relu": 127 | act_fn = nn.LeakyReLU 128 | elif activation == "tanh": 129 | act_fn = nn.Tanh 130 | elif activation == "elu": 131 | act_fn = nn.ELU 132 | else: 133 | raise NotImplemented 134 | 135 | if normalize: 136 | conv = nn.Conv2d(ninputs, noutputs, ksize, stride=stride, padding=padding, bias=False) 137 | if normalization_type == "batch": 138 | nrm = nn.BatchNorm2d(noutputs) 139 | elif normalization_type == "instance": 140 | nrm = nn.InstanceNorm2D(noutputs) 141 | else: 142 | raise ValueError("Unkown normalization type {}".format(normalization_type)) 143 | nrm.bias.data.zero_() 144 | nrm.weight.data.fill_(1.0) 145 | self.layer = nn.Sequential(conv, nrm, act_fn()) 146 | else: 147 | conv = nn.Conv2d(ninputs, noutputs, ksize, stride=stride, padding=padding) 148 | if weight_norm: 149 | conv = nn.utils.weight_norm(conv) # TODO 150 | conv.bias.data.zero_() 151 | self.layer = nn.Sequential(conv, act_fn()) 152 | 153 | if activation == "elu": 154 | nn.init.xavier_uniform_(conv.weight.data, nn.init.calculate_gain("relu")) 155 | else: 156 | nn.init.xavier_uniform_(conv.weight.data, nn.init.calculate_gain(activation)) 157 | 158 | def forward(self, x): 159 | out = self.layer(x) 160 | return out 161 | 162 | 163 | class Autoencoder(nn.Module): 164 | def __init__(self, ninputs, noutputs, ksize=3, width=64, num_levels=3, 165 | num_convs=2, max_width=512, increase_factor=1.0, 166 | normalize=False, normalization_type="batch", 167 | output_type="linear", 168 | activation="relu", pooling="max"): 169 | super(Autoencoder, self).__init__() 170 | 171 | 172 | next_level = None 173 | for lvl in range(num_levels-1, -1, -1): 174 | n_in = min(int(width*(increase_factor)**(lvl-1)), max_width) 175 | w = min(int(width*(increase_factor)**(lvl)), max_width) 176 | n_us = min(int(width*(increase_factor)**(lvl+1)), max_width) 177 | n_out = w 178 | o_type = activation 179 | 180 | if lvl == 0: 181 | n_in = ninputs 182 | o_type = output_type 183 | n_out = noutputs 184 | elif lvl == num_levels-1: 185 | n_us = None 186 | 187 | next_level = AutoencoderLevel( 188 | n_in, n_out, next_level=next_level, num_us=n_us, 189 | ksize=ksize, width=w, num_convs=num_convs, 190 | output_type=o_type, normalize=normalize, 191 | normalization_type=normalization_type, 192 | activation=activation, pooling=pooling) 193 | 194 | self.add_module("net", next_level) 195 | 196 | def forward(self, x): 197 | return self.net(x) 198 | 199 | 200 | class AutoencoderLevel(nn.Module): 201 | def __init__(self, num_inputs, num_outputs, next_level=None, 202 | num_us=None, 203 | ksize=3, width=64, num_convs=2, output_type="linear", 204 | normalize=True, normalization_type="batch", pooling="max", 205 | activation="relu"): 206 | super(AutoencoderLevel, self).__init__() 207 | 208 | self.is_last = (next_level is None) 209 | 210 | if self.is_last: 211 | self.left = ConvChain( 212 | num_inputs, num_outputs, ksize=ksize, width=width, 213 | depth=num_convs, stride=1, pad=True, 214 | normalize=normalize, normalization_type=normalization_type, 215 | output_type=output_type) 216 | else: 217 | assert num_us is not None 218 | 219 | self.left = ConvChain( 220 | num_inputs, width, ksize=ksize, width=width, 221 | depth=num_convs, stride=1, pad=True, normalize=normalize, 222 | normalization_type=normalization_type, 223 | output_type=activation, activation=activation) 224 | if pooling == "max": 225 | self.downsample = nn.MaxPool2d(2, 2) 226 | elif pooling == "average": 227 | self.downsample = nn.AvgPool2d(2, 2) 228 | elif pooling == "conv": 229 | self.downsample = nn.Conv2d(width, width, 2, stride=2) 230 | else: 231 | raise ValueError("unknown pooling'{}'".format(pooling)) 232 | 233 | self.next_level = next_level 234 | self.upsample = nn.Upsample(scale_factor=2, mode="nearest") 235 | self.right = ConvChain( 236 | num_us + width, num_outputs, ksize=ksize, width=width, 237 | depth=num_convs, stride=1, pad=True, normalize=normalize, 238 | normalization_type=normalization_type, 239 | output_type=output_type) 240 | 241 | def forward(self, x): 242 | left = self.left(x) 243 | if self.is_last: 244 | return left 245 | 246 | ds = self.downsample(left) 247 | next_level = self.next_level(ds) 248 | us = F.upsample(next_level, size=left.shape[-2:], mode='bilinear') 249 | # us = self.upsample(next_level) 250 | concat = th.cat([us, left], 1) 251 | output = self.right(concat) 252 | return output 253 | -------------------------------------------------------------------------------- /relight_eval.sh: -------------------------------------------------------------------------------- 1 | # Runs on PyTorch 1.3.1 2 | set -e 3 | 4 | export PYTHONPATH=. 5 | 6 | 7 | ### Eval Multirelight 8 | OUTDIR=eval_output/relight 9 | mkdir -p $OUTDIR 10 | python3 -m relight.eval \ 11 | --checkpoint checkpoints/relight/epoch_13.pth \ 12 | --output $OUTDIR -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | setup( 3 | name="multilum", 4 | version="0.1", 5 | packages=find_packages(), 6 | 7 | test_suite="tests.test_multilum", 8 | 9 | author="Lukas Murmann", 10 | author_email="lmurmann@mit.edu", 11 | description="Multi Illumination Image Sets SDK", 12 | keywords="graphics vision multi-illumination", 13 | url="https://projects.csail.mit.edu/multi-illumination", 14 | project_urls={ 15 | "Source Code": "https://github.com/lmurmann/multilum_sdk", 16 | }, 17 | ) 18 | -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_0_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_0_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_10_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_10_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_11_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_11_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_12_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_12_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_13_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_13_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_14_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_14_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_15_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_15_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_16_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_16_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_17_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_17_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_18_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_18_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_19_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_19_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_1_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_1_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_20_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_20_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_21_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_21_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_22_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_22_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_23_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_23_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_24_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_24_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_2_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_2_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_3_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_3_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_4_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_4_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_5_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_5_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_6_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_6_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_7_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_7_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_8_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_8_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/dir_9_mip6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/dir_9_mip6.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/materials_mip4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/materials_mip4.png -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_0_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_0_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_10_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_10_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_11_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_11_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_12_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_12_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_13_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_13_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_14_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_14_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_15_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_15_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_16_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_16_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_17_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_17_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_18_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_18_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_19_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_19_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_1_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_1_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_20_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_20_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_21_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_21_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_22_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_22_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_23_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_23_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_24_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_24_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_2_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_2_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_3_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_3_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_4_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_4_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_5_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_5_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_6_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_6_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_7_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_7_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_8_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_8_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/probes/dir_9_chrome32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/probes/dir_9_chrome32.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2/thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2/thumb.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_0_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_0_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_10_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_10_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_11_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_11_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_12_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_12_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_13_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_13_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_14_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_14_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_15_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_15_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_16_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_16_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_17_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_17_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_18_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_18_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_19_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_19_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_1_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_1_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_20_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_20_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_21_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_21_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_22_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_22_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_23_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_23_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_24_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_24_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_2_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_2_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_3_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_3_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_4_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_4_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_5_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_5_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_6_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_6_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_7_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_7_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_8_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_8_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/dir_9_mip7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/dir_9_mip7.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_0_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_0_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_10_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_10_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_11_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_11_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_12_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_12_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_13_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_13_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_14_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_14_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_15_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_15_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_16_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_16_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_17_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_17_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_18_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_18_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_19_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_19_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_1_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_1_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_20_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_20_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_21_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_21_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_22_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_22_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_23_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_23_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_24_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_24_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_2_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_2_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_3_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_3_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_4_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_4_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_5_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_5_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_6_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_6_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_7_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_7_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_8_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_8_chrome16.jpg -------------------------------------------------------------------------------- /tests/data/everett_kitchen2_golden/probes/dir_9_chrome16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmurmann/multi_illumination/a85aa9253065ff836ea97ba1a04b14259a06b3e0/tests/data/everett_kitchen2_golden/probes/dir_9_chrome16.jpg -------------------------------------------------------------------------------- /tests/test_multilum.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import tempfile 4 | import shutil 5 | 6 | import numpy as np 7 | 8 | import multilum 9 | from multilum import query_scenes, query_rooms, query_images 10 | from multilum import query_materials, query_probes 11 | 12 | class TestMetaData(unittest.TestCase): 13 | 14 | def test_query_scenes(self): 15 | self.assertEqual(len(query_scenes()), 1015) 16 | self.assertEqual(len(query_scenes(scenes='everett_kitchen2')), 1) 17 | self.assertEqual(len(query_scenes(scenes='everett_kitchen2', 18 | room_types='bathroom')), 0) 19 | self.assertEqual(len(query_scenes(scenes=['elm_basebath2', 'elm_basebath8', 'elm_kitchen2'], 20 | room_types='bathroom')), 2) 21 | self.assertEqual(len(query_scenes(buildings=['everett', 'summer'])), 90) 22 | 23 | self.assertEqual(query_scenes('everett_kitchen2')[0].room, 24 | query_rooms("everett/kitchen")[0]) 25 | 26 | 27 | def mse(A, B): 28 | if A.dtype == 'uint8': 29 | A = A / 255 30 | if B.dtype == 'uint8': 31 | B = B / 255 32 | 33 | diff = A - B 34 | return np.sum(diff*diff) / np.prod(diff.shape) 35 | 36 | 37 | class TestImageLoader(unittest.TestCase): 38 | 39 | def setUp(self): 40 | test_src = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data") 41 | self.tmpdir = tempfile.mkdtemp() 42 | tmp_data = os.path.join(self.tmpdir, 'data') 43 | shutil.copytree(test_src, tmp_data) 44 | multilum.set_datapath(tmp_data) 45 | 46 | def tearDown(self): 47 | shutil.rmtree(self.tmpdir) 48 | 49 | def test_query_images(self): 50 | mip=6 51 | h, w = multilum.imshape(mip) 52 | 53 | I = query_images('everett_kitchen2', dirs=[1,5,7, 9], mip=mip) 54 | self.assertEqual(I.shape, (1, 4, h, w, 3)) 55 | 56 | def test_gen_mipmaps(self): 57 | generated_mip = query_images('everett_kitchen2', dirs=0, mip=7)[0,0] 58 | golden_mip = query_images('everett_kitchen2_golden', dirs=0, mip=7)[0,0] 59 | self.assertLess(mse(generated_mip, golden_mip), 1e-3) 60 | 61 | def test_gen_probes(self): 62 | """Test generating a 16px chrome ball from 32px base image. 63 | 64 | The ground truth 16px reference is stored in the "golden" directory. 65 | """ 66 | self.assertTrue(multilum.probe_is_downloaded( 67 | 'everett_kitchen2', material='chrome', size=32, hdr=False)) 68 | self.assertTrue(multilum.probe_is_downloaded( 69 | 'everett_kitchen2_golden', material='chrome', size=16, hdr=False)) 70 | 71 | multilum.generate_probe_size(['everett_kitchen2'], 72 | material='chrome', size=16, hdr=False, base_size=32) 73 | 74 | self.assertTrue(multilum.probe_is_downloaded( 75 | 'everett_kitchen2', material='chrome', size=16, hdr=False)) 76 | 77 | generated_probe = query_probes('everett_kitchen2', size=16)[0,0] 78 | golden_probe = query_probes('everett_kitchen2_golden', size=16)[0,0] 79 | self.assertLess(mse(generated_probe, golden_probe), 1e-3) 80 | 81 | 82 | class TestMaterialLoader(unittest.TestCase): 83 | def test_query_materials(self): 84 | mip = 4 85 | h, w = multilum.imshape(mip) 86 | M = query_materials('everett_kitchen2', mip=mip) 87 | self.assertEqual(M.shape, (1, h, w)) 88 | 89 | M = query_materials('everett_kitchen2', mip=mip, apply_colors=True) 90 | self.assertEqual(M.shape, (1, h, w, 3)) 91 | 92 | 93 | if __name__ == '__main__': 94 | unittest.main() 95 | --------------------------------------------------------------------------------