├── CycleGAN ├── LICENSE ├── data.py ├── download_dataset.sh ├── imlib │ ├── __init__.py │ ├── basic.py │ ├── dtype.py │ └── transform.py ├── module.py ├── pics │ ├── apple2orange.jpg │ ├── horse.gif │ ├── horse2zebra.gif │ ├── horse2zebra.jpg │ └── summer2winter.jpg ├── pylib │ ├── __init__.py │ ├── argument.py │ ├── path.py │ ├── processing.py │ ├── serialization.py │ └── timer.py ├── requirements.txt ├── test.py ├── tf2gan │ ├── __init__.py │ └── loss.py ├── tf2lib │ ├── __init__.py │ ├── data │ │ ├── __init__.py │ │ └── dataset.py │ ├── image │ │ ├── __init__.py │ │ └── image.py │ ├── ops │ │ ├── __init__.py │ │ └── ops.py │ └── utils.py └── train.py ├── README.md ├── UnityScene └── README.md └── img ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.jpg ├── 6.png ├── Screenshot.png ├── Screenshot1364.png ├── Screenshot1434.png ├── Screenshot1729.png ├── Screenshot1837.png ├── Screenshot862.png ├── img10.png ├── img7.png ├── img8.png ├── img9.png ├── mix_img.png ├── our_structure.png └── virtualps.png /CycleGAN/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Zhenliang He 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /CycleGAN/data.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import tensorflow as tf 3 | import tf2lib as tl 4 | 5 | 6 | def make_dataset(img_paths, batch_size, load_size, crop_size, training, drop_remainder=True, shuffle=True, repeat=1): 7 | if training: 8 | @tf.function 9 | def _map_fn(img): # preprocessing 10 | img = tf.image.random_flip_left_right(img) 11 | img = tf.image.resize(img, [load_size, load_size]) 12 | img = tf.image.random_crop(img, [crop_size, crop_size, tf.shape(img)[-1]]) 13 | img = tf.clip_by_value(img, 0, 255) / 255.0 # or img = tl.minmax_norm(img) 14 | img = img * 2 - 1 15 | return img 16 | else: 17 | @tf.function 18 | def _map_fn(img): # preprocessing 19 | img = tf.image.resize(img, [crop_size, crop_size]) # or img = tf.image.resize(img, [load_size, load_size]); img = tl.center_crop(img, crop_size) 20 | img = tf.clip_by_value(img, 0, 255) / 255.0 # or img = tl.minmax_norm(img) 21 | img = img * 2 - 1 22 | return img 23 | 24 | return tl.disk_image_batch_dataset(img_paths, 25 | batch_size, 26 | drop_remainder=drop_remainder, 27 | map_fn=_map_fn, 28 | shuffle=shuffle, 29 | repeat=repeat) 30 | 31 | 32 | def make_zip_dataset(A_img_paths, B_img_paths, batch_size, load_size, crop_size, training, shuffle=True, repeat=False): 33 | # zip two datasets aligned by the longer one 34 | if repeat: 35 | A_repeat = B_repeat = None # cycle both 36 | else: 37 | if len(A_img_paths) >= len(B_img_paths): 38 | A_repeat = 1 39 | B_repeat = None # cycle the shorter one 40 | else: 41 | A_repeat = None # cycle the shorter one 42 | B_repeat = 1 43 | 44 | A_dataset = make_dataset(A_img_paths, batch_size, load_size, crop_size, training, drop_remainder=True, shuffle=shuffle, repeat=A_repeat) 45 | B_dataset = make_dataset(B_img_paths, batch_size, load_size, crop_size, training, drop_remainder=True, shuffle=shuffle, repeat=B_repeat) 46 | 47 | A_B_dataset = tf.data.Dataset.zip((A_dataset, B_dataset)) 48 | len_dataset = max(len(A_img_paths), len(B_img_paths)) // batch_size 49 | 50 | return A_B_dataset, len_dataset 51 | 52 | 53 | class ItemPool: 54 | 55 | def __init__(self, pool_size=50): 56 | self.pool_size = pool_size 57 | self.items = [] 58 | 59 | def __call__(self, in_items): 60 | # `in_items` should be a batch tensor 61 | 62 | if self.pool_size == 0: 63 | return in_items 64 | 65 | out_items = [] 66 | for in_item in in_items: 67 | if len(self.items) < self.pool_size: 68 | self.items.append(in_item) 69 | out_items.append(in_item) 70 | else: 71 | if np.random.rand() > 0.5: 72 | idx = np.random.randint(0, len(self.items)) 73 | out_item, self.items[idx] = self.items[idx], in_item 74 | out_items.append(out_item) 75 | else: 76 | out_items.append(in_item) 77 | return tf.stack(out_items, axis=0) 78 | -------------------------------------------------------------------------------- /CycleGAN/download_dataset.sh: -------------------------------------------------------------------------------- 1 | mkdir datasets 2 | FILE=$1 3 | 4 | if [[ $FILE != "ae_photos" && $FILE != "apple2orange" && $FILE != "summer2winter_yosemite" && $FILE != "horse2zebra" && $FILE != "monet2photo" && $FILE != "cezanne2photo" && $FILE != "ukiyoe2photo" && $FILE != "vangogh2photo" && $FILE != "maps" && $FILE != "cityscapes" && $FILE != "facades" && $FILE != "iphone2dslr_flower" && $FILE != "ae_photos" ]]; then 5 | echo "Available datasets are: apple2orange, summer2winter_yosemite, horse2zebra, monet2photo, cezanne2photo, ukiyoe2photo, vangogh2photo, maps, cityscapes, facades, iphone2dslr_flower, ae_photos" 6 | exit 1 7 | fi 8 | 9 | URL=https://people.eecs.berkeley.edu/~taesung_park/CycleGAN/datasets/$FILE.zip 10 | ZIP_FILE=./datasets/$FILE.zip 11 | TARGET_DIR=./datasets/$FILE/ 12 | wget -N $URL -O $ZIP_FILE 13 | mkdir $TARGET_DIR 14 | unzip $ZIP_FILE -d ./datasets/ 15 | rm $ZIP_FILE 16 | -------------------------------------------------------------------------------- /CycleGAN/imlib/__init__.py: -------------------------------------------------------------------------------- 1 | from imlib.basic import * 2 | from imlib.dtype import * 3 | from imlib.transform import * 4 | -------------------------------------------------------------------------------- /CycleGAN/imlib/basic.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import skimage.io as iio 3 | 4 | from imlib import dtype 5 | 6 | 7 | def imread(path, as_gray=False, **kwargs): 8 | """Return a float64 image in [-1.0, 1.0].""" 9 | image = iio.imread(path, as_gray, **kwargs) 10 | if image.dtype == np.uint8: 11 | image = image / 127.5 - 1 12 | elif image.dtype == np.uint16: 13 | image = image / 32767.5 - 1 14 | elif image.dtype in [np.float32, np.float64]: 15 | image = image * 2 - 1.0 16 | else: 17 | raise Exception("Inavailable image dtype: %s!" % image.dtype) 18 | return image 19 | 20 | 21 | def imwrite(image, path, quality=95, **plugin_args): 22 | """Save a [-1.0, 1.0] image.""" 23 | iio.imsave(path, dtype.im2uint(image), quality=quality, **plugin_args) 24 | 25 | 26 | def imshow(image): 27 | """Show a [-1.0, 1.0] image.""" 28 | iio.imshow(dtype.im2uint(image)) 29 | 30 | 31 | show = iio.show 32 | -------------------------------------------------------------------------------- /CycleGAN/imlib/dtype.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def _check(images, dtypes, min_value=-np.inf, max_value=np.inf): 5 | # check type 6 | assert isinstance(images, np.ndarray), '`images` should be np.ndarray!' 7 | 8 | # check dtype 9 | dtypes = dtypes if isinstance(dtypes, (list, tuple)) else [dtypes] 10 | assert images.dtype in dtypes, 'dtype of `images` shoud be one of %s!' % dtypes 11 | 12 | # check nan and inf 13 | assert np.all(np.isfinite(images)), '`images` contains NaN or Inf!' 14 | 15 | # check value 16 | if min_value not in [None, -np.inf]: 17 | l = '[' + str(min_value) 18 | else: 19 | l = '(-inf' 20 | min_value = -np.inf 21 | if max_value not in [None, np.inf]: 22 | r = str(max_value) + ']' 23 | else: 24 | r = 'inf)' 25 | max_value = np.inf 26 | assert np.min(images) >= min_value and np.max(images) <= max_value, \ 27 | '`images` should be in the range of %s!' % (l + ',' + r) 28 | 29 | 30 | def to_range(images, min_value=0.0, max_value=1.0, dtype=None): 31 | """Transform images from [-1.0, 1.0] to [min_value, max_value] of dtype.""" 32 | _check(images, [np.float32, np.float64], -1.0, 1.0) 33 | dtype = dtype if dtype else images.dtype 34 | return ((images + 1.) / 2. * (max_value - min_value) + min_value).astype(dtype) 35 | 36 | 37 | def float2im(images): 38 | """Transform images from [0, 1.0] to [-1.0, 1.0].""" 39 | _check(images, [np.float32, np.float64], 0.0, 1.0) 40 | return images * 2 - 1.0 41 | 42 | 43 | def float2uint(images): 44 | """Transform images from [0, 1.0] to uint8.""" 45 | _check(images, [np.float32, np.float64], -0.0, 1.0) 46 | return (images * 255).astype(np.uint8) 47 | 48 | 49 | def im2uint(images): 50 | """Transform images from [-1.0, 1.0] to uint8.""" 51 | return to_range(images, 0, 255, np.uint8) 52 | 53 | 54 | def im2float(images): 55 | """Transform images from [-1.0, 1.0] to [0.0, 1.0].""" 56 | return to_range(images, 0.0, 1.0) 57 | 58 | 59 | def uint2im(images): 60 | """Transform images from uint8 to [-1.0, 1.0] of float64.""" 61 | _check(images, np.uint8) 62 | return images / 127.5 - 1.0 63 | 64 | 65 | def uint2float(images): 66 | """Transform images from uint8 to [0.0, 1.0] of float64.""" 67 | _check(images, np.uint8) 68 | return images / 255.0 69 | 70 | 71 | def cv2im(images): 72 | """Transform opencv images to [-1.0, 1.0].""" 73 | images = uint2im(images) 74 | return images[..., ::-1] 75 | 76 | 77 | def im2cv(images): 78 | """Transform images from [-1.0, 1.0] to opencv images.""" 79 | images = im2uint(images) 80 | return images[..., ::-1] 81 | -------------------------------------------------------------------------------- /CycleGAN/imlib/transform.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import skimage.color as color 3 | import skimage.transform as transform 4 | 5 | 6 | rgb2gray = color.rgb2gray 7 | gray2rgb = color.gray2rgb 8 | 9 | imresize = transform.resize 10 | imrescale = transform.rescale 11 | 12 | 13 | def immerge(images, n_rows=None, n_cols=None, padding=0, pad_value=0): 14 | """Merge images to an image with (n_rows * h) * (n_cols * w). 15 | 16 | Parameters 17 | ---------- 18 | images : numpy.array or object which can be converted to numpy.array 19 | Images in shape of N * H * W(* C=1 or 3). 20 | 21 | """ 22 | images = np.array(images) 23 | n = images.shape[0] 24 | if n_rows: 25 | n_rows = max(min(n_rows, n), 1) 26 | n_cols = int(n - 0.5) // n_rows + 1 27 | elif n_cols: 28 | n_cols = max(min(n_cols, n), 1) 29 | n_rows = int(n - 0.5) // n_cols + 1 30 | else: 31 | n_rows = int(n ** 0.5) 32 | n_cols = int(n - 0.5) // n_rows + 1 33 | 34 | h, w = images.shape[1], images.shape[2] 35 | shape = (h * n_rows + padding * (n_rows - 1), 36 | w * n_cols + padding * (n_cols - 1)) 37 | if images.ndim == 4: 38 | shape += (images.shape[3],) 39 | img = np.full(shape, pad_value, dtype=images.dtype) 40 | 41 | for idx, image in enumerate(images): 42 | i = idx % n_cols 43 | j = idx // n_cols 44 | img[j * (h + padding):j * (h + padding) + h, 45 | i * (w + padding):i * (w + padding) + w, ...] = image 46 | 47 | return img 48 | -------------------------------------------------------------------------------- /CycleGAN/module.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import tensorflow_addons as tfa 3 | import tensorflow.keras as keras 4 | 5 | 6 | # ============================================================================== 7 | # = networks = 8 | # ============================================================================== 9 | 10 | def _get_norm_layer(norm): 11 | if norm == 'none': 12 | return lambda: lambda x: x 13 | elif norm == 'batch_norm': 14 | return keras.layers.BatchNormalization 15 | elif norm == 'instance_norm': 16 | return tfa.layers.InstanceNormalization 17 | elif norm == 'layer_norm': 18 | return tfa.layers.LayerNormalization 19 | 20 | 21 | class Pad(keras.layers.Layer): 22 | 23 | def __init__(self, paddings, mode='CONSTANT', constant_values=0, **kwargs): 24 | super(Pad, self).__init__(**kwargs) 25 | self.paddings = paddings 26 | self.mode = mode 27 | self.constant_values = constant_values 28 | 29 | def call(self, inputs): 30 | return tf.pad(inputs, self.paddings, mode=self.mode, constant_values=self.constant_values) 31 | 32 | def ResnetEncoder(input_shape=(256, 256, 3), 33 | output_channels=3, 34 | dim=64, 35 | n_downsamplings=2, 36 | n_blocks=9, 37 | norm='instance_norm'): 38 | Norm = _get_norm_layer(norm) 39 | 40 | def _residual_block(x): 41 | dim = x.shape[-1] 42 | h = x 43 | 44 | h = Pad([[0, 0], [1, 1], [1, 1], [0, 0]], mode='REFLECT')(h) 45 | h = keras.layers.Conv2D(dim, 3, padding='valid', use_bias=False)(h) 46 | h = Norm()(h) 47 | h = keras.layers.ReLU()(h) 48 | 49 | h = Pad([[0, 0], [1, 1], [1, 1], [0, 0]], mode='REFLECT')(h) 50 | h = keras.layers.Conv2D(dim, 3, padding='valid', use_bias=False)(h) 51 | h = Norm()(h) 52 | 53 | return keras.layers.add([x, h]) 54 | 55 | # 0 56 | h = inputs = keras.Input(shape=input_shape) 57 | 58 | # 1 59 | h = Pad([[0, 0], [3, 3], [3, 3], [0, 0]], mode='REFLECT')(h) 60 | h = keras.layers.Conv2D(dim, 7, padding='valid', use_bias=False)(h) 61 | h = Norm()(h) 62 | h = keras.layers.ReLU()(h) 63 | 64 | # 2 65 | for _ in range(n_downsamplings): 66 | dim *= 2 67 | h = keras.layers.Conv2D(dim, 3, strides=2, padding='same', use_bias=False)(h) 68 | h = Norm()(h) 69 | h = keras.layers.ReLU()(h) 70 | 71 | # 3 72 | for _ in range(n_blocks): 73 | h = _residual_block(h) 74 | 75 | h = keras.layers.ReLU()(h) 76 | 77 | return keras.Model(inputs=inputs, outputs=h) 78 | 79 | 80 | def ResnetGenerator(input_shape=(256, 256, 3), 81 | output_channels=3, 82 | dim=64, 83 | n_downsamplings=2, 84 | n_blocks=9, 85 | norm='instance_norm'): 86 | Norm = _get_norm_layer(norm) 87 | 88 | def _residual_block(x): 89 | dim = x.shape[-1] 90 | h = x 91 | 92 | h = Pad([[0, 0], [1, 1], [1, 1], [0, 0]], mode='REFLECT')(h) 93 | h = keras.layers.Conv2D(dim, 3, padding='valid', use_bias=False)(h) 94 | h = Norm()(h) 95 | h = keras.layers.ReLU()(h) 96 | 97 | h = Pad([[0, 0], [1, 1], [1, 1], [0, 0]], mode='REFLECT')(h) 98 | h = keras.layers.Conv2D(dim, 3, padding='valid', use_bias=False)(h) 99 | h = Norm()(h) 100 | 101 | return keras.layers.add([x, h]) 102 | 103 | # 0 104 | h = inputs = keras.Input(shape=input_shape) 105 | 106 | # 1 107 | h = Pad([[0, 0], [3, 3], [3, 3], [0, 0]], mode='REFLECT')(h) 108 | h = keras.layers.Conv2D(dim, 7, padding='valid', use_bias=False)(h) 109 | h = Norm()(h) 110 | h = keras.layers.ReLU()(h) 111 | 112 | # 2 113 | for _ in range(n_downsamplings): 114 | dim *= 2 115 | h = keras.layers.Conv2D(dim, 3, strides=2, padding='same', use_bias=False)(h) 116 | h = Norm()(h) 117 | h = keras.layers.ReLU()(h) 118 | 119 | # 3 120 | for _ in range(n_blocks): 121 | h = _residual_block(h) 122 | 123 | # 4 124 | for _ in range(n_downsamplings): 125 | dim //= 2 126 | h = keras.layers.Conv2DTranspose(dim, 3, strides=2, padding='same', use_bias=False)(h) 127 | h = Norm()(h) 128 | h = keras.layers.ReLU()(h) 129 | 130 | # 5 131 | h = Pad([[0, 0], [3, 3], [3, 3], [0, 0]], mode='REFLECT')(h) 132 | h = keras.layers.Conv2D(output_channels, 7, padding='valid')(h) 133 | h = keras.layers.Activation('tanh')(h) 134 | 135 | return keras.Model(inputs=inputs, outputs=h) 136 | 137 | 138 | def ConvDiscriminator(input_shape=(256, 256, 3), 139 | dim=64, 140 | n_downsamplings=3, 141 | norm='instance_norm'): 142 | dim_ = dim 143 | Norm = _get_norm_layer(norm) 144 | 145 | # 0 146 | h = inputs = keras.Input(shape=input_shape) 147 | 148 | # 1 149 | h = keras.layers.Conv2D(dim, 4, strides=2, padding='same')(h) 150 | h = keras.layers.LeakyReLU(alpha=0.2)(h) 151 | 152 | for _ in range(n_downsamplings - 1): 153 | dim = min(dim * 2, dim_ * 8) 154 | h = keras.layers.Conv2D(dim, 4, strides=2, padding='same', use_bias=False)(h) 155 | h = Norm()(h) 156 | h = keras.layers.LeakyReLU(alpha=0.2)(h) 157 | 158 | # 2 159 | dim = min(dim * 2, dim_ * 8) 160 | h = keras.layers.Conv2D(dim, 4, strides=1, padding='same', use_bias=False)(h) 161 | h = Norm()(h) 162 | h = keras.layers.LeakyReLU(alpha=0.2)(h) 163 | 164 | # 3 165 | h = keras.layers.Conv2D(1, 4, strides=1, padding='same')(h) 166 | 167 | return keras.Model(inputs=inputs, outputs=h) 168 | 169 | 170 | # ============================================================================== 171 | # = learning rate scheduler = 172 | # ============================================================================== 173 | 174 | class LinearDecay(keras.optimizers.schedules.LearningRateSchedule): 175 | # if `step` < `step_decay`: use fixed learning rate 176 | # else: linearly decay the learning rate to zero 177 | 178 | def __init__(self, initial_learning_rate, total_steps, step_decay): 179 | super(LinearDecay, self).__init__() 180 | self._initial_learning_rate = initial_learning_rate 181 | self._steps = total_steps 182 | self._step_decay = step_decay 183 | self.current_learning_rate = tf.Variable(initial_value=initial_learning_rate, trainable=False, dtype=tf.float32) 184 | 185 | def __call__(self, step): 186 | self.current_learning_rate.assign(tf.cond( 187 | step >= self._step_decay, 188 | true_fn=lambda: self._initial_learning_rate * (1 - 1 / (self._steps - self._step_decay) * (step - self._step_decay)), 189 | false_fn=lambda: self._initial_learning_rate 190 | )) 191 | return self.current_learning_rate 192 | -------------------------------------------------------------------------------- /CycleGAN/pics/apple2orange.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/CycleGAN/pics/apple2orange.jpg -------------------------------------------------------------------------------- /CycleGAN/pics/horse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/CycleGAN/pics/horse.gif -------------------------------------------------------------------------------- /CycleGAN/pics/horse2zebra.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/CycleGAN/pics/horse2zebra.gif -------------------------------------------------------------------------------- /CycleGAN/pics/horse2zebra.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/CycleGAN/pics/horse2zebra.jpg -------------------------------------------------------------------------------- /CycleGAN/pics/summer2winter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/CycleGAN/pics/summer2winter.jpg -------------------------------------------------------------------------------- /CycleGAN/pylib/__init__.py: -------------------------------------------------------------------------------- 1 | from pylib.argument import * 2 | from pylib.processing import * 3 | from pylib.path import * 4 | from pylib.serialization import * 5 | from pylib.timer import * 6 | 7 | import pprint 8 | 9 | pp = pprint.pprint 10 | -------------------------------------------------------------------------------- /CycleGAN/pylib/argument.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import functools 3 | import json 4 | 5 | from pylib import serialization 6 | 7 | 8 | GLOBAL_COMMAND_PARSER = argparse.ArgumentParser() 9 | 10 | 11 | def _serialization_wrapper(func): 12 | @functools.wraps(func) 13 | def _wrapper(*args, **kwargs): 14 | to_json = kwargs.pop("to_json", None) 15 | to_yaml = kwargs.pop("to_yaml", None) 16 | namespace = func(*args, **kwargs) 17 | if to_json: 18 | args_to_json(to_json, namespace) 19 | if to_yaml: 20 | args_to_yaml(to_yaml, namespace) 21 | return namespace 22 | return _wrapper 23 | 24 | 25 | def str2bool(v): 26 | if v.lower() in ('yes', 'true', 't', 'y', '1'): 27 | return True 28 | elif v.lower() in ('no', 'false', 'f', 'n', '0'): 29 | return False 30 | else: 31 | raise argparse.ArgumentTypeError('Boolean value expected!') 32 | 33 | 34 | def argument(*args, **kwargs): 35 | """Wrap argparse.add_argument.""" 36 | if 'type'in kwargs: 37 | if issubclass(kwargs['type'], bool): 38 | kwargs['type'] = str2bool 39 | elif issubclass(kwargs['type'], dict): 40 | kwargs['type'] = json.loads 41 | return GLOBAL_COMMAND_PARSER.add_argument(*args, **kwargs) 42 | 43 | 44 | arg = argument 45 | 46 | 47 | @_serialization_wrapper 48 | def args(args=None, namespace=None): 49 | """Parse args using the global parser.""" 50 | namespace = GLOBAL_COMMAND_PARSER.parse_args(args=args, namespace=namespace) 51 | return namespace 52 | 53 | 54 | @_serialization_wrapper 55 | def args_from_xxx(obj, parser, check=True): 56 | """Load args from xxx ignoring type and choices with default still valid. 57 | 58 | Parameters 59 | ---------- 60 | parser: function 61 | Should return a dict. 62 | 63 | """ 64 | dict_ = parser(obj) 65 | namespace = argparse.ArgumentParser().parse_args(args='') # '' for not to accept command line args 66 | for k, v in dict_.items(): 67 | namespace.__setattr__(k, v) 68 | return namespace 69 | 70 | 71 | args_from_dict = functools.partial(args_from_xxx, parser=lambda x: x) 72 | args_from_json = functools.partial(args_from_xxx, parser=serialization.load_json) 73 | args_from_yaml = functools.partial(args_from_xxx, parser=serialization.load_yaml) 74 | 75 | 76 | def args_to_json(path, namespace, **kwagrs): 77 | serialization.save_json(path, vars(namespace), **kwagrs) 78 | 79 | 80 | def args_to_yaml(path, namespace, **kwagrs): 81 | serialization.save_yaml(path, vars(namespace), **kwagrs) 82 | -------------------------------------------------------------------------------- /CycleGAN/pylib/path.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import fnmatch 3 | import os 4 | import glob as _glob 5 | import sys 6 | 7 | 8 | def add_path(paths): 9 | if not isinstance(paths, (list, tuple)): 10 | paths = [paths] 11 | for path in paths: 12 | if path not in sys.path: 13 | sys.path.insert(0, path) 14 | 15 | 16 | def mkdir(paths): 17 | if not isinstance(paths, (list, tuple)): 18 | paths = [paths] 19 | for path in paths: 20 | if not os.path.exists(path): 21 | os.makedirs(path) 22 | 23 | 24 | def split(path): 25 | """Return dir, name, ext.""" 26 | dir, name_ext = os.path.split(path) 27 | name, ext = os.path.splitext(name_ext) 28 | return dir, name, ext 29 | 30 | 31 | def directory(path): 32 | return split(path)[0] 33 | 34 | 35 | def name(path): 36 | return split(path)[1] 37 | 38 | 39 | def ext(path): 40 | return split(path)[2] 41 | 42 | 43 | def name_ext(path): 44 | return ''.join(split(path)[1:]) 45 | 46 | 47 | def change_ext(path, ext): 48 | if ext[0] == '.': 49 | ext = ext[1:] 50 | return os.path.splitext(path)[0] + '.' + ext 51 | 52 | 53 | asbpath = os.path.abspath 54 | 55 | 56 | join = os.path.join 57 | 58 | 59 | def prefix(path, prefixes, sep='-'): 60 | prefixes = prefixes if isinstance(prefixes, (list, tuple)) else [prefixes] 61 | dir, name, ext = split(path) 62 | return join(dir, sep.join(prefixes) + sep + name + ext) 63 | 64 | 65 | def suffix(path, suffixes, sep='-'): 66 | suffixes = suffixes if isinstance(suffixes, (list, tuple)) else [suffixes] 67 | dir, name, ext = split(path) 68 | return join(dir, name + sep + sep.join(suffixes) + ext) 69 | 70 | 71 | def prefix_now(path, fmt="%Y-%m-%d-%H:%M:%S", sep='-'): 72 | return prefix(path, prefixes=datetime.datetime.now().strftime(fmt), sep=sep) 73 | 74 | 75 | def suffix_now(path, fmt="%Y-%m-%d-%H:%M:%S", sep='-'): 76 | return suffix(path, suffixes=datetime.datetime.now().strftime(fmt), sep=sep) 77 | 78 | 79 | def glob(dir, pats, recursive=False): # faster than match, python3 only 80 | pats = pats if isinstance(pats, (list, tuple)) else [pats] 81 | matches = [] 82 | for pat in pats: 83 | matches += _glob.glob(os.path.join(dir, pat), recursive=recursive) 84 | return matches 85 | 86 | 87 | def match(dir, pats, recursive=False): # slow 88 | pats = pats if isinstance(pats, (list, tuple)) else [pats] 89 | 90 | iterator = list(os.walk(dir)) 91 | if not recursive: 92 | iterator = iterator[0:1] 93 | 94 | matches = [] 95 | for pat in pats: 96 | for root, _, file_names in iterator: 97 | for file_name in fnmatch.filter(file_names, pat): 98 | matches.append(os.path.join(root, file_name)) 99 | 100 | return matches 101 | -------------------------------------------------------------------------------- /CycleGAN/pylib/processing.py: -------------------------------------------------------------------------------- 1 | import concurrent.futures 2 | import functools 3 | import multiprocessing 4 | 5 | 6 | def run_parallels(work_fn, iterable, max_workers=None, chunksize=1, processing_bar=True, backend_executor=multiprocessing.Pool, debug=False): 7 | if not debug: 8 | with backend_executor(max_workers) as executor: 9 | try: 10 | works = executor.imap(work_fn, iterable, chunksize=chunksize) # for multiprocessing.Pool 11 | except: 12 | works = executor.map(work_fn, iterable, chunksize=chunksize) 13 | 14 | if processing_bar: 15 | try: 16 | import tqdm 17 | try: 18 | total = len(iterable) 19 | except: 20 | total = None 21 | works = tqdm.tqdm(works, total=total) 22 | except ImportError: 23 | print('`import tqdm` fails! Run without processing bar!') 24 | 25 | results = list(works) 26 | else: 27 | results = [work_fn(i) for i in iterable] 28 | return results 29 | 30 | run_parallels_mp = run_parallels 31 | run_parallels_cfprocess = functools.partial(run_parallels, backend_executor=concurrent.futures.ProcessPoolExecutor) 32 | run_parallels_cfthread = functools.partial(run_parallels, backend_executor=concurrent.futures.ThreadPoolExecutor) 33 | 34 | 35 | if __name__ == '__main__': 36 | import time 37 | 38 | def work(i): 39 | time.sleep(0.0001) 40 | i**i 41 | return i 42 | 43 | t = time.time() 44 | results = run_parallels_mp(work, range(10000), max_workers=2, chunksize=1, processing_bar=True, debug=False) 45 | for i in results: 46 | print(i) 47 | print(time.time() - t) 48 | -------------------------------------------------------------------------------- /CycleGAN/pylib/serialization.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import pickle 4 | 5 | 6 | def _check_ext(path, default_ext): 7 | name, ext = os.path.splitext(path) 8 | if ext == '': 9 | if default_ext[0] == '.': 10 | default_ext = default_ext[1:] 11 | path = name + '.' + default_ext 12 | return path 13 | 14 | 15 | def save_json(path, obj, **kwargs): 16 | # default 17 | if 'indent' not in kwargs: 18 | kwargs['indent'] = 4 19 | if 'separators' not in kwargs: 20 | kwargs['separators'] = (',', ': ') 21 | 22 | path = _check_ext(path, 'json') 23 | 24 | # wrap json.dump 25 | with open(path, 'w') as f: 26 | json.dump(obj, f, **kwargs) 27 | 28 | 29 | def load_json(path, **kwargs): 30 | # wrap json.load 31 | with open(path) as f: 32 | return json.load(f, **kwargs) 33 | 34 | 35 | def save_yaml(path, data, **kwargs): 36 | import oyaml as yaml 37 | 38 | path = _check_ext(path, 'yml') 39 | 40 | with open(path, 'w') as f: 41 | yaml.dump(data, f, **kwargs) 42 | 43 | 44 | def load_yaml(path, **kwargs): 45 | import oyaml as yaml 46 | with open(path) as f: 47 | return yaml.load(f, **kwargs) 48 | 49 | 50 | def save_pickle(path, obj, **kwargs): 51 | 52 | path = _check_ext(path, 'pkl') 53 | 54 | # wrap pickle.dump 55 | with open(path, 'wb') as f: 56 | pickle.dump(obj, f, **kwargs) 57 | 58 | 59 | def load_pickle(path, **kwargs): 60 | # wrap pickle.load 61 | with open(path, 'rb') as f: 62 | return pickle.load(f, **kwargs) 63 | -------------------------------------------------------------------------------- /CycleGAN/pylib/timer.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import timeit 3 | 4 | 5 | class Timer: # deprecated, use tqdm instead 6 | """A timer as a context manager. 7 | 8 | Wraps around a timer. A custom timer can be passed 9 | to the constructor. The default timer is timeit.default_timer. 10 | 11 | Note that the latter measures wall clock time, not CPU time! 12 | On Unix systems, it corresponds to time.time. 13 | On Windows systems, it corresponds to time.clock. 14 | 15 | Parameters 16 | ---------- 17 | print_at_exit : boolean 18 | If True, print when exiting context. 19 | format : str 20 | `ms`, `s` or `datetime`. 21 | 22 | References 23 | ---------- 24 | - https://github.com/brouberol/contexttimer/blob/master/contexttimer/__init__.py. 25 | 26 | 27 | """ 28 | 29 | def __init__(self, fmt='s', print_at_exit=True, timer=timeit.default_timer): 30 | assert fmt in ['ms', 's', 'datetime'], "`fmt` should be 'ms', 's' or 'datetime'!" 31 | self._fmt = fmt 32 | self._print_at_exit = print_at_exit 33 | self._timer = timer 34 | self.start() 35 | 36 | def __enter__(self): 37 | """Start the timer in the context manager scope.""" 38 | self.restart() 39 | return self 40 | 41 | def __exit__(self, exc_type, exc_value, exc_traceback): 42 | """Print the end time.""" 43 | if self._print_at_exit: 44 | print(str(self)) 45 | 46 | def __str__(self): 47 | return self.fmt(self.elapsed)[1] 48 | 49 | def start(self): 50 | self.start_time = self._timer() 51 | 52 | restart = start 53 | 54 | @property 55 | def elapsed(self): 56 | """Return the current elapsed time since last (re)start.""" 57 | return self._timer() - self.start_time 58 | 59 | def fmt(self, second): 60 | if self._fmt == 'ms': 61 | time_fmt = second * 1000 62 | time_str = '%s %s' % (time_fmt, self._fmt) 63 | elif self._fmt == 's': 64 | time_fmt = second 65 | time_str = '%s %s' % (time_fmt, self._fmt) 66 | elif self._fmt == 'datetime': 67 | time_fmt = datetime.timedelta(seconds=second) 68 | time_str = str(time_fmt) 69 | return time_fmt, time_str 70 | 71 | 72 | def timeit(run_times=1, **timer_kwargs): 73 | """Function decorator displaying the function execution time. 74 | 75 | All kwargs are the arguments taken by the Timer class constructor. 76 | 77 | """ 78 | # store Timer kwargs in local variable so the namespace isn't polluted 79 | # by different level args and kwargs 80 | 81 | def decorator(f): 82 | def wrapper(*args, **kwargs): 83 | timer_kwargs.update(print_at_exit=False) 84 | with Timer(**timer_kwargs) as t: 85 | for _ in range(run_times): 86 | out = f(*args, **kwargs) 87 | fmt = '[*] Execution time of function "%(function_name)s" for %(run_times)d runs is %(execution_time)s = %(execution_time_each)s * %(run_times)d [*]' 88 | context = {'function_name': f.__name__, 'run_times': run_times, 'execution_time': t, 'execution_time_each': t.fmt(t.elapsed / run_times)[1]} 89 | print(fmt % context) 90 | return out 91 | return wrapper 92 | 93 | return decorator 94 | 95 | 96 | if __name__ == "__main__": 97 | import time 98 | 99 | # 1 100 | print(1) 101 | with Timer() as t: 102 | time.sleep(1) 103 | print(t) 104 | time.sleep(1) 105 | 106 | with Timer(fmt='datetime') as t: 107 | time.sleep(1) 108 | 109 | # 2 110 | print(2) 111 | t = Timer(fmt='ms') 112 | time.sleep(2) 113 | print(t) 114 | 115 | t = Timer(fmt='datetime') 116 | time.sleep(1) 117 | print(t) 118 | 119 | # 3 120 | print(3) 121 | 122 | @timeit(run_times=5, fmt='s') 123 | def blah(): 124 | time.sleep(2) 125 | 126 | blah() 127 | -------------------------------------------------------------------------------- /CycleGAN/requirements.txt: -------------------------------------------------------------------------------- 1 | tensorflow-gpu==2.0.0-alpha0 2 | tensorflow-addons==0.3.1 3 | scikit-image 4 | oyaml 5 | tqdm 6 | -------------------------------------------------------------------------------- /CycleGAN/test.py: -------------------------------------------------------------------------------- 1 | import imlib as im 2 | import numpy as np 3 | import pylib as py 4 | import tensorflow as tf 5 | import tf2lib as tl 6 | 7 | import data 8 | import module 9 | 10 | # ============================================================================== 11 | # = param = 12 | # ============================================================================== 13 | 14 | py.arg('--experiment_dir') 15 | py.arg('--batch_size', type=int, default=32) 16 | test_args = py.args() 17 | args = py.args_from_yaml(py.join(test_args.experiment_dir, 'settings.yml')) 18 | args.__dict__.update(test_args.__dict__) 19 | 20 | 21 | # ============================================================================== 22 | # = test = 23 | # ============================================================================== 24 | 25 | # data 26 | A_img_paths_test = py.glob(py.join(args.datasets_dir, args.dataset, 'testA'), '*.jpg') 27 | B_img_paths_test = py.glob(py.join(args.datasets_dir, args.dataset, 'testB'), '*.jpg') 28 | A_dataset_test = data.make_dataset(A_img_paths_test, args.batch_size, args.load_size, args.crop_size, 29 | training=False, drop_remainder=False, shuffle=False, repeat=1) 30 | B_dataset_test = data.make_dataset(B_img_paths_test, args.batch_size, args.load_size, args.crop_size, 31 | training=False, drop_remainder=False, shuffle=False, repeat=1) 32 | 33 | # model 34 | G_A2B = module.ResnetGenerator(input_shape=(args.crop_size, args.crop_size, 3)) 35 | G_B2A = module.ResnetGenerator(input_shape=(args.crop_size, args.crop_size, 3)) 36 | 37 | # resotre 38 | tl.Checkpoint(dict(G_A2B=G_A2B, G_B2A=G_B2A), py.join(args.experiment_dir, 'checkpoints')).restore() 39 | 40 | 41 | @tf.function 42 | def sample_A2B(A): 43 | A2B = G_A2B(A, training=False) 44 | A2B2A = G_B2A(A2B, training=False) 45 | return A2B, A2B2A 46 | 47 | 48 | @tf.function 49 | def sample_B2A(B): 50 | B2A = G_B2A(B, training=False) 51 | B2A2B = G_A2B(B2A, training=False) 52 | return B2A, B2A2B 53 | 54 | 55 | # run 56 | save_dir = py.join(args.experiment_dir, 'samples_testing', 'A2B') 57 | py.mkdir(save_dir) 58 | i = 0 59 | for A in A_dataset_test: 60 | A2B, A2B2A = sample_A2B(A) 61 | for A_i, A2B_i, A2B2A_i in zip(A, A2B, A2B2A): 62 | img = np.concatenate([A_i.numpy(), A2B_i.numpy(), A2B2A_i.numpy()], axis=1) 63 | im.imwrite(img, py.join(save_dir, py.name_ext(A_img_paths_test[i]))) 64 | i += 1 65 | 66 | save_dir = py.join(args.experiment_dir, 'samples_testing', 'B2A') 67 | py.mkdir(save_dir) 68 | i = 0 69 | for B in B_dataset_test: 70 | B2A, B2A2B = sample_B2A(B) 71 | for B_i, B2A_i, B2A2B_i in zip(B, B2A, B2A2B): 72 | img = np.concatenate([B_i.numpy(), B2A_i.numpy(), B2A2B_i.numpy()], axis=1) 73 | im.imwrite(img, py.join(save_dir, py.name_ext(B_img_paths_test[i]))) 74 | i += 1 75 | -------------------------------------------------------------------------------- /CycleGAN/tf2gan/__init__.py: -------------------------------------------------------------------------------- 1 | from tf2gan.loss import * 2 | -------------------------------------------------------------------------------- /CycleGAN/tf2gan/loss.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | 4 | def get_gan_losses_fn(): 5 | bce = tf.losses.BinaryCrossentropy(from_logits=True) 6 | 7 | def d_loss_fn(r_logit, f_logit): 8 | r_loss = bce(tf.ones_like(r_logit), r_logit) 9 | f_loss = bce(tf.zeros_like(f_logit), f_logit) 10 | return r_loss, f_loss 11 | 12 | def g_loss_fn(f_logit): 13 | f_loss = bce(tf.ones_like(f_logit), f_logit) 14 | return f_loss 15 | 16 | return d_loss_fn, g_loss_fn 17 | 18 | 19 | def get_hinge_v1_losses_fn(): 20 | def d_loss_fn(r_logit, f_logit): 21 | r_loss = tf.reduce_mean(tf.maximum(1 - r_logit, 0)) 22 | f_loss = tf.reduce_mean(tf.maximum(1 + f_logit, 0)) 23 | return r_loss, f_loss 24 | 25 | def g_loss_fn(f_logit): 26 | f_loss = tf.reduce_mean(tf.maximum(1 - f_logit, 0)) 27 | return f_loss 28 | 29 | return d_loss_fn, g_loss_fn 30 | 31 | 32 | def get_hinge_v2_losses_fn(): 33 | def d_loss_fn(r_logit, f_logit): 34 | r_loss = tf.reduce_mean(tf.maximum(1 - r_logit, 0)) 35 | f_loss = tf.reduce_mean(tf.maximum(1 + f_logit, 0)) 36 | return r_loss, f_loss 37 | 38 | def g_loss_fn(f_logit): 39 | f_loss = tf.reduce_mean(- f_logit) 40 | return f_loss 41 | 42 | return d_loss_fn, g_loss_fn 43 | 44 | 45 | def get_lsgan_losses_fn(): 46 | mse = tf.losses.MeanSquaredError() 47 | 48 | def d_loss_fn(r_logit, f_logit): 49 | r_loss = mse(tf.ones_like(r_logit), r_logit) 50 | f_loss = mse(tf.zeros_like(f_logit), f_logit) 51 | return r_loss, f_loss 52 | 53 | def g_loss_fn(f_logit): 54 | f_loss = mse(tf.ones_like(f_logit), f_logit) 55 | return f_loss 56 | 57 | return d_loss_fn, g_loss_fn 58 | 59 | 60 | def get_wgan_losses_fn(): 61 | def d_loss_fn(r_logit, f_logit): 62 | r_loss = - tf.reduce_mean(r_logit) 63 | f_loss = tf.reduce_mean(f_logit) 64 | return r_loss, f_loss 65 | 66 | def g_loss_fn(f_logit): 67 | f_loss = - tf.reduce_mean(f_logit) 68 | return f_loss 69 | 70 | return d_loss_fn, g_loss_fn 71 | 72 | 73 | def get_adversarial_losses_fn(mode): 74 | if mode == 'gan': 75 | return get_gan_losses_fn() 76 | elif mode == 'hinge_v1': 77 | return get_hinge_v1_losses_fn() 78 | elif mode == 'hinge_v2': 79 | return get_hinge_v2_losses_fn() 80 | elif mode == 'lsgan': 81 | return get_lsgan_losses_fn() 82 | elif mode == 'wgan': 83 | return get_wgan_losses_fn() 84 | 85 | 86 | def gradient_penalty(f, real, fake, mode): 87 | def _gradient_penalty(f, real, fake=None): 88 | def _interpolate(a, b=None): 89 | if b is None: # interpolation in DRAGAN 90 | beta = tf.random.uniform(shape=tf.shape(a), minval=0., maxval=1.) 91 | b = a + 0.5 * tf.math.reduce_std(a) * beta 92 | shape = [tf.shape(a)[0]] + [1] * (a.shape.ndims - 1) 93 | alpha = tf.random.uniform(shape=shape, minval=0., maxval=1.) 94 | inter = a + alpha * (b - a) 95 | inter.set_shape(a.shape) 96 | return inter 97 | 98 | x = _interpolate(real, fake) 99 | with tf.GradientTape() as t: 100 | t.watch(x) 101 | pred = f(x) 102 | grad = t.gradient(pred, x) 103 | norm = tf.norm(tf.reshape(grad, [tf.shape(grad)[0], -1]), axis=1) 104 | gp = tf.reduce_mean((norm - 1.)**2) 105 | 106 | return gp 107 | 108 | if mode == 'none': 109 | gp = tf.constant(0, dtype=real.dtype) 110 | elif mode == 'dragan': 111 | gp = _gradient_penalty(f, real) 112 | elif mode == 'wgan-gp': 113 | gp = _gradient_penalty(f, real, fake) 114 | 115 | return gp 116 | -------------------------------------------------------------------------------- /CycleGAN/tf2lib/__init__.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | from tf2lib.data import * 4 | from tf2lib.image import * 5 | from tf2lib.ops import * 6 | from tf2lib.utils import * 7 | 8 | tf.config.gpu.set_per_process_memory_growth(enabled=True) 9 | -------------------------------------------------------------------------------- /CycleGAN/tf2lib/data/__init__.py: -------------------------------------------------------------------------------- 1 | from tf2lib.data.dataset import * 2 | -------------------------------------------------------------------------------- /CycleGAN/tf2lib/data/dataset.py: -------------------------------------------------------------------------------- 1 | import multiprocessing 2 | 3 | import tensorflow as tf 4 | 5 | 6 | def batch_dataset(dataset, 7 | batch_size, 8 | drop_remainder=True, 9 | n_prefetch_batch=1, 10 | filter_fn=None, 11 | map_fn=None, 12 | n_map_threads=None, 13 | filter_after_map=False, 14 | shuffle=True, 15 | shuffle_buffer_size=None, 16 | repeat=None): 17 | # set defaults 18 | if n_map_threads is None: 19 | n_map_threads = multiprocessing.cpu_count() 20 | if shuffle and shuffle_buffer_size is None: 21 | shuffle_buffer_size = max(batch_size * 128, 2048) # set the minimum buffer size as 2048 22 | 23 | # [*] it is efficient to conduct `shuffle` before `map`/`filter` because `map`/`filter` is sometimes costly 24 | if shuffle: 25 | dataset = dataset.shuffle(shuffle_buffer_size) 26 | 27 | if not filter_after_map: 28 | if filter_fn: 29 | dataset = dataset.filter(filter_fn) 30 | 31 | if map_fn: 32 | dataset = dataset.map(map_fn, num_parallel_calls=n_map_threads) 33 | 34 | else: # [*] this is slower 35 | if map_fn: 36 | dataset = dataset.map(map_fn, num_parallel_calls=n_map_threads) 37 | 38 | if filter_fn: 39 | dataset = dataset.filter(filter_fn) 40 | 41 | dataset = dataset.batch(batch_size, drop_remainder=drop_remainder) 42 | 43 | dataset = dataset.repeat(repeat).prefetch(n_prefetch_batch) 44 | 45 | return dataset 46 | 47 | 48 | def memory_data_batch_dataset(memory_data, 49 | batch_size, 50 | drop_remainder=True, 51 | n_prefetch_batch=1, 52 | filter_fn=None, 53 | map_fn=None, 54 | n_map_threads=None, 55 | filter_after_map=False, 56 | shuffle=True, 57 | shuffle_buffer_size=None, 58 | repeat=None): 59 | """Batch dataset of memory data. 60 | 61 | Parameters 62 | ---------- 63 | memory_data : nested structure of tensors/ndarrays/lists 64 | 65 | """ 66 | dataset = tf.data.Dataset.from_tensor_slices(memory_data) 67 | dataset = batch_dataset(dataset, 68 | batch_size, 69 | drop_remainder=drop_remainder, 70 | n_prefetch_batch=n_prefetch_batch, 71 | filter_fn=filter_fn, 72 | map_fn=map_fn, 73 | n_map_threads=n_map_threads, 74 | filter_after_map=filter_after_map, 75 | shuffle=shuffle, 76 | shuffle_buffer_size=shuffle_buffer_size, 77 | repeat=repeat) 78 | return dataset 79 | 80 | 81 | def disk_image_batch_dataset(img_paths, 82 | batch_size, 83 | labels=None, 84 | drop_remainder=True, 85 | n_prefetch_batch=1, 86 | filter_fn=None, 87 | map_fn=None, 88 | n_map_threads=None, 89 | filter_after_map=False, 90 | shuffle=True, 91 | shuffle_buffer_size=None, 92 | repeat=None): 93 | """Batch dataset of disk image for PNG and JPEG. 94 | 95 | Parameters 96 | ---------- 97 | img_paths : 1d-tensor/ndarray/list of str 98 | labels : nested structure of tensors/ndarrays/lists 99 | 100 | """ 101 | if labels is None: 102 | memory_data = img_paths 103 | else: 104 | memory_data = (img_paths, labels) 105 | 106 | def parse_fn(path, *label): 107 | img = tf.io.read_file(path) 108 | img = tf.image.decode_png(img, 3) # fix channels to 3 109 | return (img,) + label 110 | 111 | if map_fn: # fuse `map_fn` and `parse_fn` 112 | def map_fn_(*args): 113 | return map_fn(*parse_fn(*args)) 114 | else: 115 | map_fn_ = parse_fn 116 | 117 | dataset = memory_data_batch_dataset(memory_data, 118 | batch_size, 119 | drop_remainder=drop_remainder, 120 | n_prefetch_batch=n_prefetch_batch, 121 | filter_fn=filter_fn, 122 | map_fn=map_fn_, 123 | n_map_threads=n_map_threads, 124 | filter_after_map=filter_after_map, 125 | shuffle=shuffle, 126 | shuffle_buffer_size=shuffle_buffer_size, 127 | repeat=repeat) 128 | 129 | return dataset 130 | -------------------------------------------------------------------------------- /CycleGAN/tf2lib/image/__init__.py: -------------------------------------------------------------------------------- 1 | from tf2lib.image.image import * 2 | -------------------------------------------------------------------------------- /CycleGAN/tf2lib/image/image.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import random 3 | 4 | import tensorflow as tf 5 | 6 | 7 | def center_crop(image, size): 8 | # for image of shape [batch, height, width, channels] or [height, width, channels] 9 | if not isinstance(size, (tuple, list)): 10 | size = [size, size] 11 | offset_height = (tf.shape(image)[-3] - size[0]) // 2 12 | offset_width = (tf.shape(image)[-2] - size[1]) // 2 13 | return tf.image.crop_to_bounding_box(image, offset_height, offset_width, size[0], size[1]) 14 | 15 | 16 | def color_jitter(image, brightness=0, contrast=0, saturation=0, hue=0): 17 | """Color jitter. 18 | 19 | Examples 20 | -------- 21 | >>> color_jitter(img, 25, 0.2, 0.2, 0.1) 22 | 23 | """ 24 | tforms = [] 25 | if brightness > 0: 26 | tforms.append(functools.partial(tf.image.random_brightness, max_delta=brightness)) 27 | if contrast > 0: 28 | tforms.append(functools.partial(tf.image.random_contrast, lower=max(0, 1 - contrast), upper=1 + contrast)) 29 | if saturation > 0: 30 | tforms.append(functools.partial(tf.image.random_saturation, lower=max(0, 1 - saturation), upper=1 + saturation)) 31 | if hue > 0: 32 | tforms.append(functools.partial(tf.image.random_hue, max_delta=hue)) 33 | 34 | random.shuffle(tforms) 35 | for tform in tforms: 36 | image = tform(image) 37 | 38 | return image 39 | 40 | 41 | def random_grayscale(image, p=0.1): 42 | return tf.cond(pred=tf.random.uniform(shape=(), dtype=tf.float32) < p, 43 | true_fn=lambda: tf.image.adjust_saturation(image, 0), 44 | false_fn=lambda: image) 45 | -------------------------------------------------------------------------------- /CycleGAN/tf2lib/ops/__init__.py: -------------------------------------------------------------------------------- 1 | from tf2lib.ops.ops import * 2 | -------------------------------------------------------------------------------- /CycleGAN/tf2lib/ops/ops.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | 4 | def minmax_norm(x, epsilon=1e-12): 5 | x = tf.cast(x, tf.float32) 6 | min_val = tf.reduce_min(x) 7 | max_val = tf.reduce_max(x) 8 | norm_x = (x - min_val) / tf.maximum((max_val - min_val), epsilon) 9 | return norm_x 10 | 11 | 12 | def reshape(x, shape): 13 | x = tf.convert_to_tensor(x) 14 | shape = [x.shape[i] if shape[i] == 0 else shape[i] for i in range(len(shape))] 15 | shape = [tf.shape(x)[i] if shape[i] is None else shape[i] for i in range(len(shape))] 16 | return tf.reshape(x, shape) 17 | -------------------------------------------------------------------------------- /CycleGAN/tf2lib/utils.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | 4 | class Checkpoint: 5 | """Enhanced "tf.train.Checkpoint".""" 6 | 7 | def __init__(self, 8 | checkpoint_kwargs, # for "tf.train.Checkpoint" 9 | directory, # for "tf.train.CheckpointManager" 10 | max_to_keep=5, 11 | keep_checkpoint_every_n_hours=None): 12 | self.checkpoint = tf.train.Checkpoint(**checkpoint_kwargs) 13 | self.manager = tf.train.CheckpointManager(self.checkpoint, directory, max_to_keep, keep_checkpoint_every_n_hours) 14 | 15 | def restore(self, save_path=None): 16 | save_path = self.manager.latest_checkpoint if save_path is None else save_path 17 | return self.checkpoint.restore(save_path) 18 | 19 | def save(self, file_prefix_or_checkpoint_number=None, session=None): 20 | if isinstance(file_prefix_or_checkpoint_number, str): 21 | return self.checkpoint.save(file_prefix_or_checkpoint_number, session=session) 22 | else: 23 | return self.manager.save(checkpoint_number=file_prefix_or_checkpoint_number) 24 | 25 | 26 | def summary(name_data_dict, 27 | step=None, 28 | types=['mean', 'std', 'max', 'min', 'sparsity', 'histogram'], 29 | historgram_buckets=None, 30 | name='summary'): 31 | """Summary. 32 | 33 | Examples 34 | -------- 35 | >>> summary({'a': data_a, 'b': data_b}) 36 | """ 37 | def _summary(name, data): 38 | if data.shape == (): 39 | tf.summary.scalar(name, data, step=step) 40 | else: 41 | if 'mean' in types: 42 | tf.summary.scalar(name + '-mean', tf.math.reduce_mean(data), step=step) 43 | if 'std' in types: 44 | tf.summary.scalar(name + '-std', tf.math.reduce_std(data), step=step) 45 | if 'max' in types: 46 | tf.summary.scalar(name + '-max', tf.math.reduce_max(data), step=step) 47 | if 'min' in types: 48 | tf.summary.scalar(name + '-min', tf.math.reduce_min(data), step=step) 49 | if 'sparsity' in types: 50 | tf.summary.scalar(name + '-sparsity', tf.math.zero_fraction(data), step=step) 51 | if 'histogram' in types: 52 | tf.summary.histogram(name, data, step=step, buckets=historgram_buckets) 53 | 54 | with tf.name_scope(name): 55 | for name, data in name_data_dict.items(): 56 | _summary(name, data) 57 | -------------------------------------------------------------------------------- /CycleGAN/train.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | import imlib as im 4 | import numpy as np 5 | import pylib as py 6 | import tensorflow as tf 7 | import tensorflow.keras as keras 8 | import tf2lib as tl 9 | import tf2gan as gan 10 | import tqdm 11 | 12 | import data 13 | import module 14 | 15 | 16 | # ============================================================================== 17 | # = param = 18 | # ============================================================================== 19 | 20 | py.arg('--dataset', default='horse2zebra') 21 | py.arg('--datasets_dir', default='datasets') 22 | py.arg('--load_size', type=int, default=600) # load image to this size 23 | py.arg('--crop_size', type=int, default=256) # then crop to this size 24 | py.arg('--batch_size', type=int, default=1) 25 | py.arg('--epochs', type=int, default=200) 26 | py.arg('--epoch_decay', type=int, default=100) # epoch to start decaying learning rate 27 | py.arg('--lr', type=float, default=0.0002) 28 | py.arg('--beta_1', type=float, default=0.5) 29 | py.arg('--adversarial_loss_mode', default='lsgan', choices=['gan', 'hinge_v1', 'hinge_v2', 'lsgan', 'wgan']) 30 | py.arg('--gradient_penalty_mode', default='none', choices=['none', 'dragan', 'wgan-gp']) 31 | py.arg('--gradient_penalty_weight', type=float, default=10.0) 32 | py.arg('--cycle_loss_weight', type=float, default=10.0) 33 | py.arg('--identity_loss_weight', type=float, default=0.1) 34 | py.arg('--feature_loss_weight', type=float, default=0.2) 35 | py.arg('--pool_size', type=int, default=50) # pool size to store fake samples 36 | args = py.args() 37 | 38 | # output_dir 39 | output_dir = py.join('output', args.dataset) 40 | py.mkdir(output_dir) 41 | 42 | # save settings 43 | py.args_to_yaml(py.join(output_dir, 'settings.yml'), args) 44 | 45 | 46 | # ============================================================================== 47 | # = data = 48 | # ============================================================================== 49 | 50 | A_img_paths = py.glob(py.join(args.datasets_dir, args.dataset, 'trainA'), '*.png') 51 | B_img_paths = py.glob(py.join(args.datasets_dir, args.dataset, 'trainB'), '*.jpg') 52 | A_B_dataset, len_dataset = data.make_zip_dataset(A_img_paths, B_img_paths, args.batch_size, args.load_size, args.crop_size, training=True, repeat=False) 53 | 54 | A2B_pool = data.ItemPool(args.pool_size) 55 | B2A_pool = data.ItemPool(args.pool_size) 56 | 57 | A_img_paths_test = py.glob(py.join(args.datasets_dir, args.dataset, 'testA'), '*.png') 58 | B_img_paths_test = py.glob(py.join(args.datasets_dir, args.dataset, 'testB'), '*.jpg') 59 | A_B_dataset_test, _ = data.make_zip_dataset(A_img_paths_test, B_img_paths_test, args.batch_size, args.load_size, args.crop_size, training=False, repeat=True) 60 | 61 | 62 | # ============================================================================== 63 | # = models = 64 | # ============================================================================== 65 | 66 | G_A2B = module.ResnetGenerator(input_shape=(args.crop_size, args.crop_size, 3)) 67 | G_B2A = module.ResnetGenerator(input_shape=(args.crop_size, args.crop_size, 3)) 68 | 69 | F_A = module.ResnetEncoder(input_shape=(args.crop_size, args.crop_size, 3)) 70 | F_B = module.ResnetEncoder(input_shape=(args.crop_size, args.crop_size, 3)) 71 | 72 | D_A = module.ConvDiscriminator(input_shape=(args.crop_size, args.crop_size, 3)) 73 | D_B = module.ConvDiscriminator(input_shape=(args.crop_size, args.crop_size, 3)) 74 | 75 | d_loss_fn, g_loss_fn = gan.get_adversarial_losses_fn(args.adversarial_loss_mode) 76 | cycle_loss_fn = tf.losses.MeanAbsoluteError() 77 | identity_loss_fn = tf.losses.MeanAbsoluteError() 78 | 79 | G_lr_scheduler = module.LinearDecay(args.lr, args.epochs * len_dataset, args.epoch_decay * len_dataset) 80 | D_lr_scheduler = module.LinearDecay(args.lr, args.epochs * len_dataset, args.epoch_decay * len_dataset) 81 | G_optimizer = keras.optimizers.Adam(learning_rate=G_lr_scheduler, beta_1=args.beta_1) 82 | D_optimizer = keras.optimizers.Adam(learning_rate=D_lr_scheduler, beta_1=args.beta_1) 83 | 84 | 85 | # ============================================================================== 86 | # = train step = 87 | # ============================================================================== 88 | 89 | @tf.function 90 | def train_G(A, B): 91 | with tf.GradientTape() as t: 92 | A2B = G_A2B(A, training=True) 93 | B2A = G_B2A(B, training=True) 94 | A2B2A = G_B2A(A2B, training=True) 95 | B2A2B = G_A2B(B2A, training=True) 96 | 97 | G_A_Feature = F_A(A2B, training=True) 98 | B_Feature = F_B(B, training=True) 99 | 100 | A2B_d_logits = D_B(A2B, training=True) 101 | B2A_d_logits = D_A(B2A, training=True) 102 | 103 | A2B_g_loss = 2*g_loss_fn(A2B_d_logits) 104 | B2A_g_loss = g_loss_fn(B2A_d_logits) 105 | A2B2A_cycle_loss = cycle_loss_fn(A, A2B2A) 106 | B2A2B_cycle_loss = cycle_loss_fn(B, B2A2B) 107 | A2B_id_loss = identity_loss_fn(A, A2B) 108 | B2A_id_loss = identity_loss_fn(B, B2A) 109 | G_A_B_Feature_loss = identity_loss_fn(G_A_Feature, B_Feature) 110 | 111 | G_loss = (A2B_g_loss + B2A_g_loss) + (A2B2A_cycle_loss + B2A2B_cycle_loss) * args.cycle_loss_weight + (A2B_id_loss + B2A_id_loss) * args.identity_loss_weight + G_A_B_Feature_loss*args.feature_loss_weight 112 | 113 | G_grad = t.gradient(G_loss, G_A2B.trainable_variables + G_B2A.trainable_variables) 114 | G_optimizer.apply_gradients(zip(G_grad, G_A2B.trainable_variables + G_B2A.trainable_variables)) 115 | 116 | return A2B, B2A, {'A2B_g_loss': A2B_g_loss, 117 | 'B2A_g_loss': B2A_g_loss, 118 | 'A2B2A_cycle_loss': A2B2A_cycle_loss, 119 | 'B2A2B_cycle_loss': B2A2B_cycle_loss, 120 | 'A2B_id_loss': A2B_id_loss, 121 | 'B2A_id_loss': B2A_id_loss} 122 | 123 | 124 | @tf.function 125 | def train_D(A, B, A2B, B2A): 126 | with tf.GradientTape() as t: 127 | A_d_logits = D_A(A, training=True) 128 | B2A_d_logits = D_A(B2A, training=True) 129 | B_d_logits = D_B(B, training=True) 130 | A2B_d_logits = D_B(A2B, training=True) 131 | 132 | A_d_loss, B2A_d_loss = d_loss_fn(A_d_logits, B2A_d_logits) 133 | B_d_loss, A2B_d_loss = d_loss_fn(B_d_logits, A2B_d_logits) 134 | D_A_gp = gan.gradient_penalty(functools.partial(D_A, training=True), A, B2A, mode=args.gradient_penalty_mode) 135 | D_B_gp = gan.gradient_penalty(functools.partial(D_B, training=True), B, A2B, mode=args.gradient_penalty_mode) 136 | 137 | D_loss = (A_d_loss + B2A_d_loss) + (B_d_loss + A2B_d_loss) + (D_A_gp + D_B_gp) * args.gradient_penalty_weight 138 | 139 | D_grad = t.gradient(D_loss, D_A.trainable_variables + D_B.trainable_variables) 140 | D_optimizer.apply_gradients(zip(D_grad, D_A.trainable_variables + D_B.trainable_variables)) 141 | 142 | return {'A_d_loss': A_d_loss + B2A_d_loss, 143 | 'B_d_loss': B_d_loss + A2B_d_loss, 144 | 'D_A_gp': D_A_gp, 145 | 'D_B_gp': D_B_gp} 146 | 147 | 148 | def train_step(A, B): 149 | A2B, B2A, G_loss_dict = train_G(A, B) 150 | 151 | # cannot autograph `A2B_pool` 152 | A2B = A2B_pool(A2B) # or A2B = A2B_pool(A2B.numpy()), but it is much slower 153 | B2A = B2A_pool(B2A) # because of the communication between CPU and GPU 154 | 155 | D_loss_dict = train_D(A, B, A2B, B2A) 156 | 157 | return G_loss_dict, D_loss_dict 158 | 159 | 160 | @tf.function 161 | def sample(A, B): 162 | A2B = G_A2B(A, training=False) 163 | B2A = G_B2A(B, training=False) 164 | A2B2A = G_B2A(A2B, training=False) 165 | B2A2B = G_A2B(B2A, training=False) 166 | return A2B, B2A, A2B2A, B2A2B 167 | 168 | 169 | # ============================================================================== 170 | # = run = 171 | # ============================================================================== 172 | 173 | # epoch counter 174 | ep_cnt = tf.Variable(initial_value=0, trainable=False, dtype=tf.int64) 175 | 176 | # checkpoint 177 | checkpoint = tl.Checkpoint(dict(G_A2B=G_A2B, 178 | G_B2A=G_B2A, 179 | D_A=D_A, 180 | D_B=D_B, 181 | G_optimizer=G_optimizer, 182 | D_optimizer=D_optimizer, 183 | ep_cnt=ep_cnt), 184 | py.join(output_dir, 'checkpoints'), 185 | max_to_keep=5) 186 | try: # restore checkpoint including the epoch counter 187 | checkpoint.restore().assert_existing_objects_matched() 188 | except Exception as e: 189 | print(e) 190 | 191 | # summary 192 | train_summary_writer = tf.summary.create_file_writer(py.join(output_dir, 'summaries', 'train')) 193 | 194 | # sample 195 | test_iter = iter(A_B_dataset_test) 196 | sample_dir = py.join(output_dir, 'samples_training') 197 | py.mkdir(sample_dir) 198 | 199 | # main loop 200 | with train_summary_writer.as_default(): 201 | for ep in tqdm.trange(args.epochs, desc='Epoch Loop'): 202 | if ep < ep_cnt: 203 | continue 204 | 205 | # update epoch counter 206 | ep_cnt.assign_add(1) 207 | 208 | # train for an epoch 209 | for A, B in tqdm.tqdm(A_B_dataset, desc='Inner Epoch Loop', total=len_dataset): 210 | G_loss_dict, D_loss_dict = train_step(A, B) 211 | 212 | # # summary 213 | tl.summary(G_loss_dict, step=G_optimizer.iterations, name='G_losses') 214 | tl.summary(D_loss_dict, step=G_optimizer.iterations, name='D_losses') 215 | tl.summary({'learning rate': G_lr_scheduler.current_learning_rate}, step=G_optimizer.iterations, name='learning rate') 216 | 217 | # sample 218 | if G_optimizer.iterations.numpy() % 100 == 0: 219 | A, B = next(test_iter) 220 | A2B, B2A, A2B2A, B2A2B = sample(A, B) 221 | img = im.immerge(np.concatenate([A, A2B, A2B2A, B, B2A, B2A2B], axis=0), n_rows=2) 222 | im.imwrite(img, py.join(sample_dir, 'iter-%09d.jpg' % G_optimizer.iterations.numpy())) 223 | 224 | # save checkpoint 225 | checkpoint.save(ep) 226 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Parking-Slot Image Generator 2 | 3 | Unity Scenes are created by @[Mei Luo](https://github.com/sukamay) & @[Pomevak](https://github.com/Pomevak) 4 | 5 | # 1. How to run this project 6 | 7 | ### Down dataset 8 | 9 | You can download our generated dataset: 10 | 11 | Part1: https://pan.baidu.com/s/18YWtrUbaAX6GILk9eA4-1w , password: t7ha (539M) 12 | 13 | Part2: https://pan.baidu.com/s/1k3Cp66aRIrW_m9EeW5tUUA (192M) 14 | 15 | Then download the real scene dataset: 16 | 17 | https://drive.google.com/open?id=1i1pSnkIRyTgt6kmWr_sN6jrasX2HngWc (2.2G) 18 | 19 | You can divide the dataset by yourself, a sample: 20 | 21 | ![](./img/4.png) 22 | 23 | ### Configure environment 24 | 25 | Our project is mainly based on the **[CycleGAN-Tensorflow-2](https://github.com/LynnHo/CycleGAN-Tensorflow-2)** by [@LynnHo](https://github.com/LynnHo), and the prerequisities are the same: 26 | 27 | - Ubuntu 16.04 LTS 28 | - Python 3.6 29 | - CUDA 10.0 & cuDNN 7.4 30 | - tensorflow-gpu 2.0.0-alpha0 31 | - tensorflow-addons 32 | - scikit-image, oyaml, tqdm 33 | 34 | You can install the python package: `pip install -r requirements.txt` 35 | 36 | ### Run train and test 37 | 38 | You can see all of the input arguments in `train.py` & `test.py` 39 | 40 | Train: 41 | 42 | ```bash 43 | CUDA_VISIBLE_DEVICES=0 python train.py --dataset unity2real 44 | ``` 45 | 46 | Test: 47 | 48 | ```bash 49 | CUDA_VISIBLE_DEVICES=0 python test.py --experiment_dir ./output/unity2real 50 | ``` 51 | 52 | ### Output sample 53 | 54 | You can see the output sample at `./output/unity2real/sample_training` 55 | 56 | ![](./img/5.jpg) 57 | 58 | # 2. Introduction 59 | 60 | ## 2.1 Backgroud 61 | 62 | > Domain adaptation is a hot research topic in the fields of machine learning and computer vision. Roughly speaking, methods in this area can be categorized into two classes, pixel-level ones and feature-level ones. Image-level domain adaptation aims to learn the mapping between different visual domains. It is quite useful for augmenting training sets. In this project, your task is to complete a system which can create virtual parking-slot image samples that are realistic looking. 63 | 64 | 65 | 66 | ## 2.2 Requirements 67 | 68 | > 1. Develop a virtual parking-slot image generation system using Unity. Taking a real road image as the texture. Then, feed the texture into Unity. Your system is expected to create various parking-slot image samples by adjusting the lighting conditions, the trees, the objects nearby, and the parking-lines. 69 | > 70 | > ![img](./img/virtualps.png) 71 | > 72 | >   73 | > 74 | > 2. Virtual image samples may be not as real as samples collected from the real world. So, you need to use GAN-based domain adaptation technologies to improve their reality. You may try the following suggested candidate solutions: 1) Learning from simulated and unsupervised images through adversarial training, CVPR 2017; 2) Unsupervised pixel-level domain adaptation with generative adversarial networks, CVPR 2017; 3) Diverse image-to-image translation via disentangled representations, ECCV 2018. To train GAN-based pixel-level domain adaptation networks, you may need to use real parking-slot image samples, which can be obtained here https://cslinzhang.github.io/deepps/. 75 | 76 | 77 | 78 | # 3. Solution 79 | 80 | ## 3.1 Generate dataset 81 | 82 | We use unity to construct some virtual scenes. 83 | 84 | According to the requirements, we generated parking space pictures for different scenes. 85 | 86 | We mainly classify parking spaces as follows: 87 | 88 | 1. Lighting: We simulated the changes in lighting under one day, especially in the case of strong light during the day and sunset 89 | 2. Weather: sunny, rainy, cloudy, we have made a weather control system to change the weather in one scene. 90 | 3. Shade: There may be shade of tree or other tall buildings on the road. 91 | 4. Obstacles: cars, pedestrians, manhole covers on the ground, fire rafts, short bushes, street lights, etc. 92 | 5. Direction of the parking space: horizontal, lateral, oblique 93 | 6. Parking line: solid line, dotted line, different mottled degree 94 | 7. Ground texture: cement floor, masonry ground, shallow lawn, mooring road surface, etc. 95 | 96 | We mainly set up the following scenarios. 97 | 98 | ![Screenshot862](./img/mix_img.png) 99 | 100 | 101 | 102 | ## 3.2 Domain adapation 103 | 104 | ### CycleGAN[^1] 105 | 106 | ![](./img/1.png) 107 | 108 | CycleGAN is a creative network to solve the unpaired pix2pix domain adapation problem. 109 | 110 | The innovation of CycleGAN is that it enables this migration between the source and target domains without the need for one-to-one mapping between training data. 111 | 112 | In this method, the image in the source domain is transformed by two steps: 113 | 114 | - Map the source image to the target domain 115 | 116 | - Returne the generated image in target domain to the source domain, get the generated image, thus eliminating the requirement of image pairing in the target domain. 117 | 118 | Using the generator network to map the image to the target domain, and by matching the generator and discriminator, the quality of the generated image can be improved. 119 | 120 | 121 | 122 | ### CyCADA[^2] 123 | 124 | ![](./img/2.png) 125 | 126 | CyCADA is a new network structure based on CycleGAN, which adapts representations at both the pixel-level and feature-level. By adding feature loss & semantic loss, CyCADA can perform better on domain adaptation problem, especially on the dataset with semantic information. And the experiment designed by the authors is a transform from GTA game screenshots to real scene, similar to our project. 127 | 128 | ![](./img/3.png) 129 | 130 | 131 | 132 | ### Our network 133 | 134 | Since our dataset doesn't have the semantic information, so I just add feature loss to CycleGAN, use resnet as the encoder to extract feature map. Then Compare the features from A domain and regenerated B domain images, I set the initial feature loss weight as 0.2, you can change that to adjust the influence by feature loss. ` --feature_loss_weight` 135 | 136 | ```python 137 | # module.py 138 | def ResnetEncoder(input_shape=(256, 256, 3), 139 | output_channels=3, 140 | dim=64, 141 | n_downsamplings=2, 142 | n_blocks=9, 143 | norm='instance_norm'): 144 | Norm = _get_norm_layer(norm) 145 | 146 | def _residual_block(x): 147 | dim = x.shape[-1] 148 | h = x 149 | 150 | h = Pad([[0, 0], [1, 1], [1, 1], [0, 0]], mode='REFLECT')(h) 151 | h = keras.layers.Conv2D(dim, 3, padding='valid', use_bias=False)(h) 152 | h = Norm()(h) 153 | h = keras.layers.ReLU()(h) 154 | 155 | h = Pad([[0, 0], [1, 1], [1, 1], [0, 0]], mode='REFLECT')(h) 156 | h = keras.layers.Conv2D(dim, 3, padding='valid', use_bias=False)(h) 157 | h = Norm()(h) 158 | 159 | return keras.layers.add([x, h]) 160 | 161 | # 0 162 | h = inputs = keras.Input(shape=input_shape) 163 | 164 | # 1 165 | h = Pad([[0, 0], [3, 3], [3, 3], [0, 0]], mode='REFLECT')(h) 166 | h = keras.layers.Conv2D(dim, 7, padding='valid', use_bias=False)(h) 167 | h = Norm()(h) 168 | h = keras.layers.ReLU()(h) 169 | 170 | # 2 171 | for _ in range(n_downsamplings): 172 | dim *= 2 173 | h = keras.layers.Conv2D(dim, 3, strides=2, padding='same', use_bias=False)(h) 174 | h = Norm()(h) 175 | h = keras.layers.ReLU()(h) 176 | 177 | # 3 178 | for _ in range(n_blocks): 179 | h = _residual_block(h) 180 | 181 | h = keras.layers.ReLU()(h) 182 | 183 | return keras.Model(inputs=inputs, outputs=h) 184 | ``` 185 | 186 | ```python 187 | # train.py 188 | A2B = G_A2B(A, training=True) 189 | B2A = G_B2A(B, training=True) 190 | A2B2A = G_B2A(A2B, training=True) 191 | B2A2B = G_A2B(B2A, training=True) 192 | 193 | G_A_Feature = F_A(A2B, training=True) # Get the feature of generated image from A 194 | B_Feature = F_B(B, training=True) # Get the feature of B image 195 | 196 | A2B_d_logits = D_B(A2B, training=True) 197 | B2A_d_logits = D_A(B2A, training=True) 198 | 199 | A2B_g_loss = 2*g_loss_fn(A2B_d_logits) 200 | B2A_g_loss = g_loss_fn(B2A_d_logits) 201 | A2B2A_cycle_loss = cycle_loss_fn(A, A2B2A) 202 | B2A2B_cycle_loss = cycle_loss_fn(B, B2A2B) 203 | A2B_id_loss = identity_loss_fn(A, A2B) 204 | B2A_id_loss = identity_loss_fn(B, B2A) 205 | G_A_B_Feature_loss = identity_loss_fn(G_A_Feature, B_Feature) # Calculate the loss between two features 206 | 207 | G_loss = (A2B_g_loss + B2A_g_loss) + (A2B2A_cycle_loss + B2A2B_cycle_loss) * args.cycle_loss_weight + (A2B_id_loss + B2A_id_loss) * args.identity_loss_weight + G_A_B_Feature_loss*args.feature_loss_weight 208 | ``` 209 | 210 | **Result** 211 | 212 | ![](./img/6.png) 213 | 214 | ## 4. References 215 | ### CycleGAN 216 | 217 | #### Paper: 218 | 219 | [^1]: J. Zhu, et al, *[Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks](https://arxiv.org/abs/1703.10593)*, 2017 International Conference on Computer Vision (ICCV 2017), IEEE, 2017. 220 | 221 | #### Codes: 222 | 223 | https://github.com/LynnHo/CycleGAN-Tensorflow-2 224 | 225 | ### CyCADA: 226 | 227 | #### Paper: 228 | 229 | [^2]: J. Hoffman, et al, *[CyCADA: Cycle Consistent Adversarial Domain Adaptation](https://arxiv.org/abs/1703.10593)*, 2018 International Conference on Machine Learning (ICML 2018), IEEE, 2018. 230 | 231 | #### Codes: 232 | 233 | https://github.com/jhoffman/cycada_release 234 | 235 | -------------------------------------------------------------------------------- /UnityScene/README.md: -------------------------------------------------------------------------------- 1 | # Unity Scenes 2 | 3 | You can download the unity package: https://pan.baidu.com/s/1TPTRYEXJ_Tf90gvIGjI1CA , password: 5ybe 4 | 5 | Then import it to your unity. 6 | 7 | -------------------------------------------------------------------------------- /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/1.png -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/3.png -------------------------------------------------------------------------------- /img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/4.png -------------------------------------------------------------------------------- /img/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/5.jpg -------------------------------------------------------------------------------- /img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/6.png -------------------------------------------------------------------------------- /img/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/Screenshot.png -------------------------------------------------------------------------------- /img/Screenshot1364.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/Screenshot1364.png -------------------------------------------------------------------------------- /img/Screenshot1434.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/Screenshot1434.png -------------------------------------------------------------------------------- /img/Screenshot1729.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/Screenshot1729.png -------------------------------------------------------------------------------- /img/Screenshot1837.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/Screenshot1837.png -------------------------------------------------------------------------------- /img/Screenshot862.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/Screenshot862.png -------------------------------------------------------------------------------- /img/img10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/img10.png -------------------------------------------------------------------------------- /img/img7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/img7.png -------------------------------------------------------------------------------- /img/img8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/img8.png -------------------------------------------------------------------------------- /img/img9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/img9.png -------------------------------------------------------------------------------- /img/mix_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/mix_img.png -------------------------------------------------------------------------------- /img/our_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/our_structure.png -------------------------------------------------------------------------------- /img/virtualps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dinghow/Parking-Slot_Image_Generator/79dafe4e81e1935557cf58b84008c8f21f009c1c/img/virtualps.png --------------------------------------------------------------------------------