Spaces:
Build error
Build error
Upload 28 files
Browse files- data/__init__.py +0 -0
- data/aligned_dataset.py +78 -0
- data/base_data_loader.py +14 -0
- data/base_dataset.py +90 -0
- data/custom_dataset_data_loader.py +31 -0
- data/data_loader.py +7 -0
- data/image_folder.py +65 -0
- diffusion.py +387 -0
- interface.py +38 -0
- models/__init__.py +0 -0
- models/base_model.py +91 -0
- models/models.py +20 -0
- models/networks.py +416 -0
- models/pix2pixHD_model.py +306 -0
- models/ui_model.py +347 -0
- options/__init__.py +0 -0
- options/base_options.py +99 -0
- options/test_options.py +17 -0
- options/train_options.py +34 -0
- pix2pixhd_test.py +67 -0
- pixelcnn/gated_pixelcnn.py +153 -0
- pixelcnn/models.py +143 -0
- requirements.txt +3 -0
- util/__init__.py +0 -0
- util/html.py +63 -0
- util/image_pool.py +31 -0
- util/util.py +100 -0
- util/visualizer.py +131 -0
data/__init__.py
ADDED
|
File without changes
|
data/aligned_dataset.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os.path
|
| 2 |
+
from data.base_dataset import BaseDataset, get_params, get_transform, normalize
|
| 3 |
+
from data.image_folder import make_dataset
|
| 4 |
+
from PIL import Image
|
| 5 |
+
|
| 6 |
+
class AlignedDataset(BaseDataset):
|
| 7 |
+
def initialize(self, opt):
|
| 8 |
+
self.opt = opt
|
| 9 |
+
self.root = opt.dataroot
|
| 10 |
+
|
| 11 |
+
### input A (label maps)
|
| 12 |
+
dir_A = '_masks' if self.opt.label_nc == 0 else '_label'
|
| 13 |
+
self.dir_A = os.path.join(opt.dataroot, opt.phase + dir_A)
|
| 14 |
+
self.A_paths = sorted(make_dataset(self.dir_A))
|
| 15 |
+
|
| 16 |
+
### input B (real images)
|
| 17 |
+
if opt.isTrain or opt.use_encoded_image:
|
| 18 |
+
self.A_paths = [path for path in self.A_paths if "pos_" in path] # neg for benign, pos for malignant
|
| 19 |
+
dir_B = '_images' if self.opt.label_nc == 0 else '_img'
|
| 20 |
+
self.dir_B = os.path.join(opt.dataroot, opt.phase + dir_B)
|
| 21 |
+
self.B_paths = sorted(make_dataset(self.dir_B))
|
| 22 |
+
self.B_paths = [path for path in self.B_paths if "pos_" in path] #neg for benign, pos for malignant
|
| 23 |
+
|
| 24 |
+
### instance maps
|
| 25 |
+
if not opt.no_instance:
|
| 26 |
+
self.dir_inst = os.path.join(opt.dataroot, opt.phase + '_inst')
|
| 27 |
+
self.inst_paths = sorted(make_dataset(self.dir_inst))
|
| 28 |
+
|
| 29 |
+
### load precomputed instance-wise encoded features
|
| 30 |
+
if opt.load_features:
|
| 31 |
+
self.dir_feat = os.path.join(opt.dataroot, opt.phase + '_feat')
|
| 32 |
+
print('----------- loading features from %s ----------' % self.dir_feat)
|
| 33 |
+
self.feat_paths = sorted(make_dataset(self.dir_feat))
|
| 34 |
+
|
| 35 |
+
self.dataset_size = len(self.A_paths)
|
| 36 |
+
|
| 37 |
+
def __getitem__(self, index):
|
| 38 |
+
### input A (label maps)
|
| 39 |
+
A_path = self.A_paths[index]
|
| 40 |
+
A = Image.open(A_path)
|
| 41 |
+
params = get_params(self.opt, A.size)
|
| 42 |
+
if self.opt.label_nc == 0:
|
| 43 |
+
transform_A = get_transform(self.opt, params)
|
| 44 |
+
A_tensor = transform_A(A.convert('RGB'))
|
| 45 |
+
else:
|
| 46 |
+
transform_A = get_transform(self.opt, params, method=Image.NEAREST, normalize=False)
|
| 47 |
+
A_tensor = transform_A(A) * 255.0
|
| 48 |
+
|
| 49 |
+
B_tensor = inst_tensor = feat_tensor = 0
|
| 50 |
+
### input B (real images)
|
| 51 |
+
if self.opt.isTrain or self.opt.use_encoded_image:
|
| 52 |
+
B_path = self.B_paths[index]
|
| 53 |
+
B = Image.open(B_path).convert('RGB')
|
| 54 |
+
transform_B = get_transform(self.opt, params)
|
| 55 |
+
B_tensor = transform_B(B)
|
| 56 |
+
|
| 57 |
+
### if using instance maps
|
| 58 |
+
if not self.opt.no_instance:
|
| 59 |
+
inst_path = self.inst_paths[index]
|
| 60 |
+
inst = Image.open(inst_path)
|
| 61 |
+
inst_tensor = transform_A(inst)
|
| 62 |
+
|
| 63 |
+
if self.opt.load_features:
|
| 64 |
+
feat_path = self.feat_paths[index]
|
| 65 |
+
feat = Image.open(feat_path).convert('RGB')
|
| 66 |
+
norm = normalize()
|
| 67 |
+
feat_tensor = norm(transform_A(feat))
|
| 68 |
+
|
| 69 |
+
input_dict = {'label': A_tensor, 'inst': inst_tensor, 'image': B_tensor,
|
| 70 |
+
'feat': feat_tensor, 'path': A_path}
|
| 71 |
+
|
| 72 |
+
return input_dict
|
| 73 |
+
|
| 74 |
+
def __len__(self):
|
| 75 |
+
return len(self.A_paths) // self.opt.batchSize * self.opt.batchSize
|
| 76 |
+
|
| 77 |
+
def name(self):
|
| 78 |
+
return 'AlignedDataset'
|
data/base_data_loader.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
class BaseDataLoader():
|
| 3 |
+
def __init__(self):
|
| 4 |
+
pass
|
| 5 |
+
|
| 6 |
+
def initialize(self, opt):
|
| 7 |
+
self.opt = opt
|
| 8 |
+
pass
|
| 9 |
+
|
| 10 |
+
def load_data():
|
| 11 |
+
return None
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
|
data/base_dataset.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch.utils.data as data
|
| 2 |
+
from PIL import Image
|
| 3 |
+
import torchvision.transforms as transforms
|
| 4 |
+
import numpy as np
|
| 5 |
+
import random
|
| 6 |
+
|
| 7 |
+
class BaseDataset(data.Dataset):
|
| 8 |
+
def __init__(self):
|
| 9 |
+
super(BaseDataset, self).__init__()
|
| 10 |
+
|
| 11 |
+
def name(self):
|
| 12 |
+
return 'BaseDataset'
|
| 13 |
+
|
| 14 |
+
def initialize(self, opt):
|
| 15 |
+
pass
|
| 16 |
+
|
| 17 |
+
def get_params(opt, size):
|
| 18 |
+
w, h = size
|
| 19 |
+
new_h = h
|
| 20 |
+
new_w = w
|
| 21 |
+
if opt.resize_or_crop == 'resize_and_crop':
|
| 22 |
+
new_h = new_w = opt.loadSize
|
| 23 |
+
elif opt.resize_or_crop == 'scale_width_and_crop':
|
| 24 |
+
new_w = opt.loadSize
|
| 25 |
+
new_h = opt.loadSize * h // w
|
| 26 |
+
|
| 27 |
+
x = random.randint(0, np.maximum(0, new_w - opt.fineSize))
|
| 28 |
+
y = random.randint(0, np.maximum(0, new_h - opt.fineSize))
|
| 29 |
+
|
| 30 |
+
flip = random.random() > 0.5
|
| 31 |
+
return {'crop_pos': (x, y), 'flip': flip}
|
| 32 |
+
|
| 33 |
+
def get_transform(opt, params, method=Image.BICUBIC, normalize=True):
|
| 34 |
+
transform_list = []
|
| 35 |
+
if 'resize' in opt.resize_or_crop:
|
| 36 |
+
osize = [opt.loadSize, opt.loadSize]
|
| 37 |
+
transform_list.append(transforms.Scale(osize, method))
|
| 38 |
+
elif 'scale_width' in opt.resize_or_crop:
|
| 39 |
+
transform_list.append(transforms.Lambda(lambda img: __scale_width(img, opt.loadSize, method)))
|
| 40 |
+
|
| 41 |
+
if 'crop' in opt.resize_or_crop:
|
| 42 |
+
transform_list.append(transforms.Lambda(lambda img: __crop(img, params['crop_pos'], opt.fineSize)))
|
| 43 |
+
|
| 44 |
+
if opt.resize_or_crop == 'none':
|
| 45 |
+
base = float(2 ** opt.n_downsample_global)
|
| 46 |
+
if opt.netG == 'local':
|
| 47 |
+
base *= (2 ** opt.n_local_enhancers)
|
| 48 |
+
transform_list.append(transforms.Lambda(lambda img: __make_power_2(img, base, method)))
|
| 49 |
+
|
| 50 |
+
if opt.isTrain and not opt.no_flip:
|
| 51 |
+
transform_list.append(transforms.Lambda(lambda img: __flip(img, params['flip'])))
|
| 52 |
+
|
| 53 |
+
transform_list += [transforms.ToTensor()]
|
| 54 |
+
|
| 55 |
+
if normalize:
|
| 56 |
+
transform_list += [transforms.Normalize((0.5, 0.5, 0.5),
|
| 57 |
+
(0.5, 0.5, 0.5))]
|
| 58 |
+
return transforms.Compose(transform_list)
|
| 59 |
+
|
| 60 |
+
def normalize():
|
| 61 |
+
return transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
|
| 62 |
+
|
| 63 |
+
def __make_power_2(img, base, method=Image.BICUBIC):
|
| 64 |
+
ow, oh = img.size
|
| 65 |
+
h = int(round(oh / base) * base)
|
| 66 |
+
w = int(round(ow / base) * base)
|
| 67 |
+
if (h == oh) and (w == ow):
|
| 68 |
+
return img
|
| 69 |
+
return img.resize((w, h), method)
|
| 70 |
+
|
| 71 |
+
def __scale_width(img, target_width, method=Image.BICUBIC):
|
| 72 |
+
ow, oh = img.size
|
| 73 |
+
if (ow == target_width):
|
| 74 |
+
return img
|
| 75 |
+
w = target_width
|
| 76 |
+
h = int(target_width * oh / ow)
|
| 77 |
+
return img.resize((w, h), method)
|
| 78 |
+
|
| 79 |
+
def __crop(img, pos, size):
|
| 80 |
+
ow, oh = img.size
|
| 81 |
+
x1, y1 = pos
|
| 82 |
+
tw = th = size
|
| 83 |
+
if (ow > tw or oh > th):
|
| 84 |
+
return img.crop((x1, y1, x1 + tw, y1 + th))
|
| 85 |
+
return img
|
| 86 |
+
|
| 87 |
+
def __flip(img, flip):
|
| 88 |
+
if flip:
|
| 89 |
+
return img.transpose(Image.FLIP_LEFT_RIGHT)
|
| 90 |
+
return img
|
data/custom_dataset_data_loader.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch.utils.data
|
| 2 |
+
from data.base_data_loader import BaseDataLoader
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def CreateDataset(opt):
|
| 6 |
+
dataset = None
|
| 7 |
+
from data.aligned_dataset import AlignedDataset
|
| 8 |
+
dataset = AlignedDataset()
|
| 9 |
+
|
| 10 |
+
print("dataset [%s] was created" % (dataset.name()))
|
| 11 |
+
dataset.initialize(opt)
|
| 12 |
+
return dataset
|
| 13 |
+
|
| 14 |
+
class CustomDatasetDataLoader(BaseDataLoader):
|
| 15 |
+
def name(self):
|
| 16 |
+
return 'CustomDatasetDataLoader'
|
| 17 |
+
|
| 18 |
+
def initialize(self, opt):
|
| 19 |
+
BaseDataLoader.initialize(self, opt)
|
| 20 |
+
self.dataset = CreateDataset(opt)
|
| 21 |
+
self.dataloader = torch.utils.data.DataLoader(
|
| 22 |
+
self.dataset,
|
| 23 |
+
batch_size=opt.batchSize,
|
| 24 |
+
shuffle=not opt.serial_batches,
|
| 25 |
+
num_workers=int(opt.nThreads))
|
| 26 |
+
|
| 27 |
+
def load_data(self):
|
| 28 |
+
return self.dataloader
|
| 29 |
+
|
| 30 |
+
def __len__(self):
|
| 31 |
+
return min(len(self.dataset), self.opt.max_dataset_size)
|
data/data_loader.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
def CreateDataLoader(opt):
|
| 3 |
+
from data.custom_dataset_data_loader import CustomDatasetDataLoader
|
| 4 |
+
data_loader = CustomDatasetDataLoader()
|
| 5 |
+
print(data_loader.name())
|
| 6 |
+
data_loader.initialize(opt)
|
| 7 |
+
return data_loader
|
data/image_folder.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
###############################################################################
|
| 2 |
+
# Code from
|
| 3 |
+
# https://github.com/pytorch/vision/blob/master/torchvision/datasets/folder.py
|
| 4 |
+
# Modified the original code so that it also loads images from the current
|
| 5 |
+
# directory as well as the subdirectories
|
| 6 |
+
###############################################################################
|
| 7 |
+
import torch.utils.data as data
|
| 8 |
+
from PIL import Image
|
| 9 |
+
import os
|
| 10 |
+
|
| 11 |
+
IMG_EXTENSIONS = [
|
| 12 |
+
'.jpg', '.JPG', '.jpeg', '.JPEG',
|
| 13 |
+
'.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP', '.tiff'
|
| 14 |
+
]
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def is_image_file(filename):
|
| 18 |
+
return any(filename.endswith(extension) for extension in IMG_EXTENSIONS)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def make_dataset(dir):
|
| 22 |
+
images = []
|
| 23 |
+
assert os.path.isdir(dir), '%s is not a valid directory' % dir
|
| 24 |
+
|
| 25 |
+
for root, _, fnames in sorted(os.walk(dir)):
|
| 26 |
+
for fname in fnames:
|
| 27 |
+
if is_image_file(fname):
|
| 28 |
+
path = os.path.join(root, fname)
|
| 29 |
+
images.append(path)
|
| 30 |
+
|
| 31 |
+
return images
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def default_loader(path):
|
| 35 |
+
return Image.open(path).convert('RGB')
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class ImageFolder(data.Dataset):
|
| 39 |
+
|
| 40 |
+
def __init__(self, root, transform=None, return_paths=False,
|
| 41 |
+
loader=default_loader):
|
| 42 |
+
imgs = make_dataset(root)
|
| 43 |
+
if len(imgs) == 0:
|
| 44 |
+
raise(RuntimeError("Found 0 images in: " + root + "\n"
|
| 45 |
+
"Supported image extensions are: " +
|
| 46 |
+
",".join(IMG_EXTENSIONS)))
|
| 47 |
+
|
| 48 |
+
self.root = root
|
| 49 |
+
self.imgs = imgs
|
| 50 |
+
self.transform = transform
|
| 51 |
+
self.return_paths = return_paths
|
| 52 |
+
self.loader = loader
|
| 53 |
+
|
| 54 |
+
def __getitem__(self, index):
|
| 55 |
+
path = self.imgs[index]
|
| 56 |
+
img = self.loader(path)
|
| 57 |
+
if self.transform is not None:
|
| 58 |
+
img = self.transform(img)
|
| 59 |
+
if self.return_paths:
|
| 60 |
+
return img, path
|
| 61 |
+
else:
|
| 62 |
+
return img
|
| 63 |
+
|
| 64 |
+
def __len__(self):
|
| 65 |
+
return len(self.imgs)
|
diffusion.py
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import matplotlib.pyplot as plt
|
| 3 |
+
from torchvision.utils import save_image
|
| 4 |
+
from torchvision import transforms
|
| 5 |
+
from vqvae_data.latents import LatentsDataset
|
| 6 |
+
from torch.utils.data import DataLoader
|
| 7 |
+
import numpy as np, os
|
| 8 |
+
from torch import nn
|
| 9 |
+
import math
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
from torch.optim import Adam
|
| 12 |
+
from typing import Optional
|
| 13 |
+
import random
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def mkdir(dir):
|
| 17 |
+
if not os.path.exists(dir):
|
| 18 |
+
os.makedirs(dir)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def get_beta_schedule(beta_schedule, beta_start, beta_end, num_diffusion_timesteps):
|
| 22 |
+
def sigmoid(x):
|
| 23 |
+
return 1 / (np.exp(-x) + 1)
|
| 24 |
+
|
| 25 |
+
if beta_schedule == "quad":
|
| 26 |
+
betas = (
|
| 27 |
+
np.linspace(
|
| 28 |
+
beta_start ** 0.5,
|
| 29 |
+
beta_end ** 0.5,
|
| 30 |
+
num_diffusion_timesteps,
|
| 31 |
+
dtype=np.float64,
|
| 32 |
+
)
|
| 33 |
+
** 2
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
elif beta_schedule == "linear":
|
| 37 |
+
betas = np.linspace(
|
| 38 |
+
beta_start, beta_end, num_diffusion_timesteps, dtype=np.float64
|
| 39 |
+
)
|
| 40 |
+
elif beta_schedule == "const":
|
| 41 |
+
betas = beta_end * np.ones(num_diffusion_timesteps, dtype=np.float64)
|
| 42 |
+
elif beta_schedule == "jsd": # 1/T, 1/(T-1), 1/(T-2), ..., 1
|
| 43 |
+
betas = 1.0 / np.linspace(
|
| 44 |
+
num_diffusion_timesteps, 1, num_diffusion_timesteps, dtype=np.float64
|
| 45 |
+
)
|
| 46 |
+
elif beta_schedule == "sigmoid":
|
| 47 |
+
betas = np.linspace(-6, 6, num_diffusion_timesteps)
|
| 48 |
+
betas = sigmoid(betas) * (beta_end - beta_start) + beta_start
|
| 49 |
+
else:
|
| 50 |
+
raise NotImplementedError(beta_schedule)
|
| 51 |
+
assert betas.shape == (num_diffusion_timesteps,)
|
| 52 |
+
betas = torch.from_numpy(betas).float()
|
| 53 |
+
return betas
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def get_index_from_list(vals, t, x_shape):
|
| 57 |
+
"""
|
| 58 |
+
Returns a specific index t of a passed list of values vals
|
| 59 |
+
while considering the batch dimension.
|
| 60 |
+
"""
|
| 61 |
+
batch_size = t.shape[0]
|
| 62 |
+
out = vals.gather(-1, t.cpu())
|
| 63 |
+
return out.reshape(batch_size, *((1,) * (len(x_shape) - 1))).to(t.device)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def forward_diffusion_sample(x, t, device="cpu"):
|
| 67 |
+
"""
|
| 68 |
+
Takes an image and a timestep as input and
|
| 69 |
+
returns the noisy version of it
|
| 70 |
+
"""
|
| 71 |
+
noise = torch.randn_like(x) # gaussian noise
|
| 72 |
+
# noise = torch.FloatTensor(x.shape).uniform_(-1, 1) #uniform distribution noise
|
| 73 |
+
sqrt_alphas_cumprod_t = get_index_from_list(sqrt_alphas_cumprod, t, x.shape)
|
| 74 |
+
sqrt_one_minus_alphas_cumprod_t = get_index_from_list(
|
| 75 |
+
sqrt_one_minus_alphas_cumprod, t, x.shape
|
| 76 |
+
)
|
| 77 |
+
# print("coeff stats ",sqrt_alphas_cumprod_t, " and ", sqrt_one_minus_alphas_cumprod_t)
|
| 78 |
+
# mean + variance
|
| 79 |
+
return sqrt_alphas_cumprod_t.to(device) * x.to(device) \
|
| 80 |
+
+ sqrt_one_minus_alphas_cumprod_t.to(device) * noise.to(device), noise.to(device)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class Block(nn.Module):
|
| 84 |
+
def __init__(self, in_ch, out_ch, time_emb_dim, up=False):
|
| 85 |
+
super().__init__()
|
| 86 |
+
self.time_mlp = nn.Linear(time_emb_dim, out_ch)
|
| 87 |
+
if up:
|
| 88 |
+
self.conv1 = nn.Conv2d(2 * in_ch, out_ch, 3, padding=1)
|
| 89 |
+
self.transform = nn.ConvTranspose2d(out_ch, out_ch, 4, 2, 1)
|
| 90 |
+
else:
|
| 91 |
+
self.conv1 = nn.Conv2d(in_ch, out_ch, 3, padding=1)
|
| 92 |
+
self.transform = nn.Conv2d(out_ch, out_ch, 4, 2, 1)
|
| 93 |
+
self.conv2 = nn.Conv2d(out_ch, out_ch, 3, padding=1)
|
| 94 |
+
self.bnorm1 = nn.BatchNorm2d(out_ch)
|
| 95 |
+
self.bnorm2 = nn.BatchNorm2d(out_ch)
|
| 96 |
+
self.relu = nn.LeakyReLU(0.2)
|
| 97 |
+
|
| 98 |
+
def forward(self, x, t, ):
|
| 99 |
+
# First Conv
|
| 100 |
+
h = self.bnorm1(self.relu(self.conv1(x)))
|
| 101 |
+
# Time embedding
|
| 102 |
+
time_emb = self.relu(self.time_mlp(t))
|
| 103 |
+
# Extend last 2 dimensions
|
| 104 |
+
time_emb = time_emb[(...,) + (None,) * 2]
|
| 105 |
+
# Add time channel
|
| 106 |
+
h = h + time_emb
|
| 107 |
+
# Second Conv
|
| 108 |
+
h = self.bnorm2(self.relu(self.conv2(h)))
|
| 109 |
+
# Down or Upsample
|
| 110 |
+
return self.transform(h)
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
class SinusoidalPositionEmbeddings(nn.Module):
|
| 114 |
+
def __init__(self, dim):
|
| 115 |
+
super().__init__()
|
| 116 |
+
self.dim = dim
|
| 117 |
+
|
| 118 |
+
def forward(self, time):
|
| 119 |
+
device = time.device
|
| 120 |
+
half_dim = self.dim // 2
|
| 121 |
+
embeddings = math.log(10000) / (half_dim - 1)
|
| 122 |
+
embeddings = torch.exp(torch.arange(half_dim, device=device) * -embeddings)
|
| 123 |
+
embeddings = time[:, None] * embeddings[None, :]
|
| 124 |
+
embeddings = torch.cat((embeddings.sin(), embeddings.cos()), dim=-1)
|
| 125 |
+
return embeddings
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
class CrossAttention(nn.Module):
|
| 129 |
+
"""
|
| 130 |
+
### Cross Attention Layer
|
| 131 |
+
This falls-back to self-attention when conditional embeddings are not specified.
|
| 132 |
+
"""
|
| 133 |
+
|
| 134 |
+
use_flash_attention: bool = True
|
| 135 |
+
|
| 136 |
+
def __init__(self, d_model: int, d_cond: int, n_heads: int, d_head: int, is_inplace: bool = False):
|
| 137 |
+
"""
|
| 138 |
+
:param d_model: is the input embedding size
|
| 139 |
+
:param n_heads: is the number of attention heads
|
| 140 |
+
:param d_head: is the size of a attention head
|
| 141 |
+
:param d_cond: is the size of the conditional embeddings
|
| 142 |
+
:param is_inplace: specifies whether to perform the attention softmax computation inplace to
|
| 143 |
+
save memory
|
| 144 |
+
"""
|
| 145 |
+
super().__init__()
|
| 146 |
+
|
| 147 |
+
self.is_inplace = is_inplace
|
| 148 |
+
self.n_heads = n_heads
|
| 149 |
+
self.d_head = d_head
|
| 150 |
+
|
| 151 |
+
# Attention scaling factor
|
| 152 |
+
self.scale = d_head ** -0.5
|
| 153 |
+
|
| 154 |
+
# Query, key and value mappings
|
| 155 |
+
d_attn = d_head * n_heads
|
| 156 |
+
self.to_q = nn.Linear(d_model, d_attn, bias=False)
|
| 157 |
+
self.to_k = nn.Linear(d_cond, d_attn, bias=False)
|
| 158 |
+
self.to_v = nn.Linear(d_cond, d_attn, bias=False)
|
| 159 |
+
|
| 160 |
+
# Final linear layer
|
| 161 |
+
self.to_out = nn.Sequential(nn.Linear(d_attn, d_model))
|
| 162 |
+
|
| 163 |
+
def forward(self, x: torch.Tensor, cond: Optional[torch.Tensor] = None):
|
| 164 |
+
"""
|
| 165 |
+
:param x: are the input embeddings of shape `[batch_size, height * width, d_model]`
|
| 166 |
+
:param cond: is the conditional embeddings of shape `[batch_size, n_cond, d_cond]`
|
| 167 |
+
"""
|
| 168 |
+
|
| 169 |
+
# If `cond` is `None` we perform self attention
|
| 170 |
+
has_cond = cond is not None
|
| 171 |
+
if not has_cond:
|
| 172 |
+
cond = x
|
| 173 |
+
|
| 174 |
+
# Get query, key and value vectors
|
| 175 |
+
q = self.to_q(x)
|
| 176 |
+
k = self.to_k(cond)
|
| 177 |
+
v = self.to_v(cond)
|
| 178 |
+
|
| 179 |
+
return self.normal_attention(q, k, v)
|
| 180 |
+
|
| 181 |
+
def normal_attention(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor):
|
| 182 |
+
"""
|
| 183 |
+
#### Normal Attention
|
| 184 |
+
|
| 185 |
+
:param q: are the query vectors before splitting heads, of shape `[batch_size, seq, d_attn]`
|
| 186 |
+
:param k: are the query vectors before splitting heads, of shape `[batch_size, seq, d_attn]`
|
| 187 |
+
:param v: are the query vectors before splitting heads, of shape `[batch_size, seq, d_attn]`
|
| 188 |
+
"""
|
| 189 |
+
|
| 190 |
+
# Split them to heads of shape `[batch_size, seq_len, n_heads, d_head]`
|
| 191 |
+
q = q.view(*q.shape[:2], self.n_heads, -1)
|
| 192 |
+
k = k.view(*k.shape[:2], self.n_heads, -1)
|
| 193 |
+
v = v.view(*v.shape[:2], self.n_heads, -1)
|
| 194 |
+
|
| 195 |
+
# Calculate attention $\frac{Q K^\top}{\sqrt{d_{key}}}$
|
| 196 |
+
attn = torch.einsum('bihd,bjhd->bhij', q, k) * self.scale
|
| 197 |
+
|
| 198 |
+
# Compute softmax
|
| 199 |
+
# $$\underset{seq}{softmax}\Bigg(\frac{Q K^\top}{\sqrt{d_{key}}}\Bigg)$$
|
| 200 |
+
if self.is_inplace:
|
| 201 |
+
half = attn.shape[0] // 2
|
| 202 |
+
attn[half:] = attn[half:].softmax(dim=-1)
|
| 203 |
+
attn[:half] = attn[:half].softmax(dim=-1)
|
| 204 |
+
else:
|
| 205 |
+
attn = attn.softmax(dim=-1)
|
| 206 |
+
|
| 207 |
+
# Compute attention output
|
| 208 |
+
# $$\underset{seq}{softmax}\Bigg(\frac{Q K^\top}{\sqrt{d_{key}}}\Bigg)V$$
|
| 209 |
+
out = torch.einsum('bhij,bjhd->bihd', attn, v)
|
| 210 |
+
# Reshape to `[batch_size, height * width, n_heads * d_head]`
|
| 211 |
+
out = out.reshape(*out.shape[:2], -1)
|
| 212 |
+
# Map to `[batch_size, height * width, d_model]` with a linear layer
|
| 213 |
+
return self.to_out(out)
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
class SimpleUnet(nn.Module):
|
| 217 |
+
def __init__(self):
|
| 218 |
+
super().__init__()
|
| 219 |
+
image_channels = 3
|
| 220 |
+
# down_channels = (64, 128, 256, 512, 1024)
|
| 221 |
+
# up_channels = (1024, 512, 256, 128, 64)
|
| 222 |
+
down_channels = (16, 32, 64, 128, 256)
|
| 223 |
+
up_channels = (256, 128, 64, 32, 16)
|
| 224 |
+
out_dim = 1
|
| 225 |
+
time_emb_dim = 32
|
| 226 |
+
|
| 227 |
+
# Time embedding
|
| 228 |
+
self.time_mlp = nn.Sequential(
|
| 229 |
+
SinusoidalPositionEmbeddings(time_emb_dim),
|
| 230 |
+
nn.Linear(time_emb_dim, time_emb_dim),
|
| 231 |
+
nn.ReLU()
|
| 232 |
+
)
|
| 233 |
+
|
| 234 |
+
# Initial projection
|
| 235 |
+
self.conv0 = nn.Conv2d(image_channels, down_channels[0], 3, padding=1)
|
| 236 |
+
|
| 237 |
+
# Downsample
|
| 238 |
+
self.downs = nn.ModuleList([Block(down_channels[i], down_channels[i + 1], \
|
| 239 |
+
time_emb_dim) \
|
| 240 |
+
for i in range(len(down_channels) - 1)])
|
| 241 |
+
# Upsample
|
| 242 |
+
self.ups = nn.ModuleList([Block(up_channels[i], up_channels[i + 1], \
|
| 243 |
+
time_emb_dim, up=True) \
|
| 244 |
+
for i in range(len(up_channels) - 1)])
|
| 245 |
+
|
| 246 |
+
self.silu = nn.SiLU()
|
| 247 |
+
|
| 248 |
+
self.output = nn.Conv2d(up_channels[-1], 3, out_dim)
|
| 249 |
+
|
| 250 |
+
self.apply_tanh = nn.Tanh()
|
| 251 |
+
|
| 252 |
+
self.cross_attention_module = CrossAttention(3, 32, 16, 16)
|
| 253 |
+
|
| 254 |
+
def forward(self, x, y, timestep):
|
| 255 |
+
|
| 256 |
+
# Embedd class condition using cross attention
|
| 257 |
+
batch_size = x.shape[0]
|
| 258 |
+
y = self.time_mlp(y)
|
| 259 |
+
y = y[:, None, :]
|
| 260 |
+
x = x.permute(0, 2, 3, 1).view(batch_size, IMG_SIZE * IMG_SIZE, 3)
|
| 261 |
+
x2 = x + self.cross_attention_module(x, y)
|
| 262 |
+
x2 = x2.view(batch_size, IMG_SIZE, IMG_SIZE, 3).permute(0, 3, 1, 2)
|
| 263 |
+
|
| 264 |
+
# Embedd time
|
| 265 |
+
t = self.time_mlp(timestep)
|
| 266 |
+
|
| 267 |
+
# Initial conv
|
| 268 |
+
x2 = self.conv0(x2)
|
| 269 |
+
|
| 270 |
+
# Unet
|
| 271 |
+
residual_inputs = []
|
| 272 |
+
for down in self.downs:
|
| 273 |
+
x2 = down(x2, t)
|
| 274 |
+
residual_inputs.append(x2)
|
| 275 |
+
for up in self.ups:
|
| 276 |
+
residual_x2 = residual_inputs.pop()
|
| 277 |
+
# Add residual x2 as additional channels
|
| 278 |
+
x2 = torch.cat((x2, residual_x2), dim=1)
|
| 279 |
+
x2 = up(x2, t)
|
| 280 |
+
|
| 281 |
+
x2 = self.silu(x2)
|
| 282 |
+
|
| 283 |
+
output = self.output(x2)
|
| 284 |
+
|
| 285 |
+
return output
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
def get_loss(model, x_0, t):
|
| 289 |
+
latent, condition = x_0 # both latents and condition have same shap
|
| 290 |
+
latent = latent.cuda()
|
| 291 |
+
condition = condition.cuda()
|
| 292 |
+
x_noisy, noise = forward_diffusion_sample(latent, t, device)
|
| 293 |
+
noise_pred = model(x_noisy, condition, t)
|
| 294 |
+
|
| 295 |
+
# return F.l1_loss(noise, noise_pred)
|
| 296 |
+
return F.mse_loss(noise, noise_pred)
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
@torch.no_grad()
|
| 300 |
+
def sample_timestep(x, model, y, t):
|
| 301 |
+
betas_t = get_index_from_list(betas, t, x.shape)
|
| 302 |
+
sqrt_one_minus_alphas_cumprod_t = get_index_from_list(
|
| 303 |
+
sqrt_one_minus_alphas_cumprod, t, x.shape
|
| 304 |
+
)
|
| 305 |
+
sqrt_recip_alphas_t = get_index_from_list(sqrt_recip_alphas, t, x.shape)
|
| 306 |
+
|
| 307 |
+
# Call model (current image - noise prediction)
|
| 308 |
+
model_mean = sqrt_recip_alphas_t * (
|
| 309 |
+
x - (betas_t / sqrt_one_minus_alphas_cumprod_t) * model(x, y, t)
|
| 310 |
+
)
|
| 311 |
+
posterior_variance_t = get_index_from_list(posterior_variance, t, x.shape)
|
| 312 |
+
|
| 313 |
+
# print("model prediction stats ",torch.max(model(x, y, t)), " and ", torch.min(model(x, y, t)))
|
| 314 |
+
|
| 315 |
+
if t == 0:
|
| 316 |
+
return model_mean
|
| 317 |
+
else:
|
| 318 |
+
noise = torch.randn_like(x)
|
| 319 |
+
return model_mean + torch.sqrt(posterior_variance_t) * noise
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
def show_tensor_image(image):
|
| 323 |
+
reverse_transforms = transforms.Compose([
|
| 324 |
+
transforms.Lambda(lambda t: (t + 1) / 2),
|
| 325 |
+
transforms.Lambda(lambda t: t.permute(1, 2, 0)), # CHW to HWC
|
| 326 |
+
transforms.Lambda(lambda t: t * 255.),
|
| 327 |
+
transforms.Lambda(lambda t: t.numpy().astype(np.uint8)),
|
| 328 |
+
transforms.ToPILImage(),
|
| 329 |
+
])
|
| 330 |
+
|
| 331 |
+
# Take first image of batch
|
| 332 |
+
if len(image.shape) == 4:
|
| 333 |
+
image = image[0, :, :, :]
|
| 334 |
+
plt.imshow(reverse_transforms(image))
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
def generate_latent(model_dir, cancer_type, output_dir):
|
| 338 |
+
if (cancer_type == 'benign'):
|
| 339 |
+
model_name = "digestpath_mask_benign_default.pt"
|
| 340 |
+
else:
|
| 341 |
+
model_name = "digestpath_mask_malignant_default.pt"
|
| 342 |
+
|
| 343 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
model_path = os.path.join(model_dir, model_name)
|
| 347 |
+
|
| 348 |
+
model = SimpleUnet()
|
| 349 |
+
model.to(device)
|
| 350 |
+
|
| 351 |
+
model.load_state_dict(torch.load(model_path))
|
| 352 |
+
print("model loaded")
|
| 353 |
+
model.eval()
|
| 354 |
+
|
| 355 |
+
# cancer_grade = random.randint(0, 1)
|
| 356 |
+
condition = torch.tensor([1]).cuda() # benign:0/malignant:1 grade cancer
|
| 357 |
+
# condition = torch.full([1, 1, IMG_SIZE, IMG_SIZE], condition).float().cuda()
|
| 358 |
+
img = torch.randn((1, 3, IMG_SIZE, IMG_SIZE), device=device)
|
| 359 |
+
for j in range(0, T)[::-1]:
|
| 360 |
+
t = torch.full((1,), j, device=device, dtype=torch.long)
|
| 361 |
+
img = sample_timestep(img, model, condition, t)
|
| 362 |
+
print("sampled image ", torch.max(img), " and ", torch.min(img))
|
| 363 |
+
save_image(img, os.path.join(output_dir, "sample.png"))
|
| 364 |
+
torch.save(img, os.path.join(output_dir, "sample.pt"))
|
| 365 |
+
|
| 366 |
+
# Define beta schedule
|
| 367 |
+
T = 1000
|
| 368 |
+
IMG_SIZE = 64
|
| 369 |
+
|
| 370 |
+
betas = get_beta_schedule(beta_schedule="linear",
|
| 371 |
+
beta_start=0.0001,
|
| 372 |
+
beta_end=0.02,
|
| 373 |
+
num_diffusion_timesteps=T)
|
| 374 |
+
|
| 375 |
+
# Pre-calculate different terms for closed form
|
| 376 |
+
alphas = 1. - betas
|
| 377 |
+
alphas_cumprod = torch.cumprod(alphas, axis=0)
|
| 378 |
+
sqrt_alphas_cumprod = torch.sqrt(alphas_cumprod) # root(alpha_bar)
|
| 379 |
+
sqrt_one_minus_alphas_cumprod = torch.sqrt(1. - alphas_cumprod) # root(1-alpha_bar)
|
| 380 |
+
sqrt_recip_alphas = torch.sqrt(1.0 / alphas) # 1/root(alpha)
|
| 381 |
+
alphas_cumprod_prev = F.pad(alphas_cumprod[:-1], (1, 0), value=1.0)
|
| 382 |
+
posterior_variance = betas * (1. - alphas_cumprod_prev) / (1. - alphas_cumprod)
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
# model_dir = "trained_models/diffusion"
|
| 386 |
+
# output_dir = r"F:\Datasets\DigestPath\scene_generation\all\1000\256\test\output\benign"
|
| 387 |
+
# generate_latent(model_dir, 'malignant', output_dir)
|
interface.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import glob
|
| 3 |
+
import argparse
|
| 4 |
+
import shutil
|
| 5 |
+
from PIL import Image
|
| 6 |
+
import PIL
|
| 7 |
+
import gradio as gr
|
| 8 |
+
import random
|
| 9 |
+
import numpy as np
|
| 10 |
+
from diffusion import generate_latent
|
| 11 |
+
from vq_vae import create_mask
|
| 12 |
+
|
| 13 |
+
model_dir = 'trained_models'
|
| 14 |
+
|
| 15 |
+
def create_image(cancer_type):
|
| 16 |
+
tmp_dir = "./tmp"
|
| 17 |
+
if os.path.exists(tmp_dir):
|
| 18 |
+
shutil.rmtree(tmp_dir)
|
| 19 |
+
os.makedirs(tmp_dir)
|
| 20 |
+
generate_latent(model_dir, cancer_type, tmp_dir)
|
| 21 |
+
create_mask(model_dir, "./tmp", "./tmp/test_masks")
|
| 22 |
+
os.system('python pix2pixhd_test.py --name diffusion_dp --dataroot ./tmp --label_nc 0 --no_instance --resize_or_crop none')
|
| 23 |
+
image_dir = "./tmp/diffusion_dp/test_latest/images"
|
| 24 |
+
input_label_image = Image.open(os.path.join(image_dir, "sample_input_label.jpg"))
|
| 25 |
+
synthesized_image = Image.open(os.path.join(image_dir, "sample_synthesized_image.jpg"))
|
| 26 |
+
return input_label_image, synthesized_image
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
demo = gr.Interface(
|
| 30 |
+
create_image,
|
| 31 |
+
inputs=gr.Radio(choices=["benign", "malignant"], label="Choose Type", value="benign"),
|
| 32 |
+
outputs=[gr.Image(), gr.Image()],
|
| 33 |
+
title="Diffusion based Image Generation"
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
demo.launch()
|
| 37 |
+
|
| 38 |
+
# create_image('benign')
|
models/__init__.py
ADDED
|
File without changes
|
models/base_model.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import torch
|
| 3 |
+
import sys
|
| 4 |
+
|
| 5 |
+
class BaseModel(torch.nn.Module):
|
| 6 |
+
def name(self):
|
| 7 |
+
return 'BaseModel'
|
| 8 |
+
|
| 9 |
+
def initialize(self, opt):
|
| 10 |
+
self.opt = opt
|
| 11 |
+
self.gpu_ids = opt.gpu_ids
|
| 12 |
+
self.isTrain = opt.isTrain
|
| 13 |
+
self.Tensor = torch.cuda.FloatTensor if self.gpu_ids else torch.Tensor
|
| 14 |
+
self.save_dir = os.path.join(opt.checkpoints_dir, opt.name)
|
| 15 |
+
|
| 16 |
+
def set_input(self, input):
|
| 17 |
+
self.input = input
|
| 18 |
+
|
| 19 |
+
def forward(self):
|
| 20 |
+
pass
|
| 21 |
+
|
| 22 |
+
# used in test time, no backprop
|
| 23 |
+
def test(self):
|
| 24 |
+
pass
|
| 25 |
+
|
| 26 |
+
def get_image_paths(self):
|
| 27 |
+
pass
|
| 28 |
+
|
| 29 |
+
def optimize_parameters(self):
|
| 30 |
+
pass
|
| 31 |
+
|
| 32 |
+
def get_current_visuals(self):
|
| 33 |
+
return self.input
|
| 34 |
+
|
| 35 |
+
def get_current_errors(self):
|
| 36 |
+
return {}
|
| 37 |
+
|
| 38 |
+
def save(self, label):
|
| 39 |
+
pass
|
| 40 |
+
|
| 41 |
+
# helper saving function that can be used by subclasses
|
| 42 |
+
def save_network(self, network, network_label, epoch_label, gpu_ids):
|
| 43 |
+
save_filename = '%s_net_%s.pth' % (epoch_label, network_label)
|
| 44 |
+
save_path = os.path.join(self.save_dir, save_filename)
|
| 45 |
+
torch.save(network.cpu().state_dict(), save_path)
|
| 46 |
+
if len(gpu_ids) and torch.cuda.is_available():
|
| 47 |
+
network.cuda()
|
| 48 |
+
|
| 49 |
+
# helper loading function that can be used by subclasses
|
| 50 |
+
def load_network(self, network, network_label, epoch_label, save_dir=''):
|
| 51 |
+
save_filename = '%s_net_%s.pth' % (epoch_label, network_label)
|
| 52 |
+
if not save_dir:
|
| 53 |
+
save_dir = self.save_dir
|
| 54 |
+
save_path = os.path.join(save_dir, save_filename)
|
| 55 |
+
if not os.path.isfile(save_path):
|
| 56 |
+
print('%s not exists yet!' % save_path)
|
| 57 |
+
if network_label == 'G':
|
| 58 |
+
raise('Generator must exist!')
|
| 59 |
+
else:
|
| 60 |
+
#network.load_state_dict(torch.load(save_path))
|
| 61 |
+
try:
|
| 62 |
+
network.load_state_dict(torch.load(save_path))
|
| 63 |
+
except:
|
| 64 |
+
pretrained_dict = torch.load(save_path)
|
| 65 |
+
model_dict = network.state_dict()
|
| 66 |
+
try:
|
| 67 |
+
pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}
|
| 68 |
+
network.load_state_dict(pretrained_dict)
|
| 69 |
+
if self.opt.verbose:
|
| 70 |
+
print('Pretrained network %s has excessive layers; Only loading layers that are used' % network_label)
|
| 71 |
+
except:
|
| 72 |
+
print('Pretrained network %s has fewer layers; The following are not initialized:' % network_label)
|
| 73 |
+
for k, v in pretrained_dict.items():
|
| 74 |
+
if v.size() == model_dict[k].size():
|
| 75 |
+
model_dict[k] = v
|
| 76 |
+
|
| 77 |
+
if sys.version_info >= (3,0):
|
| 78 |
+
not_initialized = set()
|
| 79 |
+
else:
|
| 80 |
+
from sets import Set
|
| 81 |
+
not_initialized = Set()
|
| 82 |
+
|
| 83 |
+
for k, v in model_dict.items():
|
| 84 |
+
if k not in pretrained_dict or v.size() != pretrained_dict[k].size():
|
| 85 |
+
not_initialized.add(k.split('.')[0])
|
| 86 |
+
|
| 87 |
+
print(sorted(not_initialized))
|
| 88 |
+
network.load_state_dict(model_dict)
|
| 89 |
+
|
| 90 |
+
def update_learning_rate():
|
| 91 |
+
pass
|
models/models.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
|
| 3 |
+
def create_model(opt):
|
| 4 |
+
if opt.model == 'pix2pixHD':
|
| 5 |
+
from .pix2pixHD_model import Pix2PixHDModel, InferenceModel
|
| 6 |
+
if opt.isTrain:
|
| 7 |
+
model = Pix2PixHDModel()
|
| 8 |
+
else:
|
| 9 |
+
model = InferenceModel()
|
| 10 |
+
else:
|
| 11 |
+
from .ui_model import UIModel
|
| 12 |
+
model = UIModel()
|
| 13 |
+
model.initialize(opt)
|
| 14 |
+
if opt.verbose:
|
| 15 |
+
print("model [%s] was created" % (model.name()))
|
| 16 |
+
|
| 17 |
+
if opt.isTrain and len(opt.gpu_ids) and not opt.fp16:
|
| 18 |
+
model = torch.nn.DataParallel(model, device_ids=opt.gpu_ids)
|
| 19 |
+
|
| 20 |
+
return model
|
models/networks.py
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
import functools
|
| 4 |
+
from torch.autograd import Variable
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
###############################################################################
|
| 8 |
+
# Functions
|
| 9 |
+
###############################################################################
|
| 10 |
+
def weights_init(m):
|
| 11 |
+
classname = m.__class__.__name__
|
| 12 |
+
if classname.find('Conv') != -1:
|
| 13 |
+
m.weight.data.normal_(0.0, 0.02)
|
| 14 |
+
elif classname.find('BatchNorm2d') != -1:
|
| 15 |
+
m.weight.data.normal_(1.0, 0.02)
|
| 16 |
+
m.bias.data.fill_(0)
|
| 17 |
+
|
| 18 |
+
def get_norm_layer(norm_type='instance'):
|
| 19 |
+
if norm_type == 'batch':
|
| 20 |
+
norm_layer = functools.partial(nn.BatchNorm2d, affine=True)
|
| 21 |
+
elif norm_type == 'instance':
|
| 22 |
+
norm_layer = functools.partial(nn.InstanceNorm2d, affine=False)
|
| 23 |
+
else:
|
| 24 |
+
raise NotImplementedError('normalization layer [%s] is not found' % norm_type)
|
| 25 |
+
return norm_layer
|
| 26 |
+
|
| 27 |
+
def define_G(input_nc, output_nc, ngf, netG, n_downsample_global=3, n_blocks_global=9, n_local_enhancers=1,
|
| 28 |
+
n_blocks_local=3, norm='instance', gpu_ids=[]):
|
| 29 |
+
norm_layer = get_norm_layer(norm_type=norm)
|
| 30 |
+
if netG == 'global':
|
| 31 |
+
netG = GlobalGenerator(input_nc, output_nc, ngf, n_downsample_global, n_blocks_global, norm_layer)
|
| 32 |
+
elif netG == 'local':
|
| 33 |
+
netG = LocalEnhancer(input_nc, output_nc, ngf, n_downsample_global, n_blocks_global,
|
| 34 |
+
n_local_enhancers, n_blocks_local, norm_layer)
|
| 35 |
+
elif netG == 'encoder':
|
| 36 |
+
netG = Encoder(input_nc, output_nc, ngf, n_downsample_global, norm_layer)
|
| 37 |
+
else:
|
| 38 |
+
raise('generator not implemented!')
|
| 39 |
+
print(netG)
|
| 40 |
+
if len(gpu_ids) > 0:
|
| 41 |
+
assert(torch.cuda.is_available())
|
| 42 |
+
netG.cuda(gpu_ids[0])
|
| 43 |
+
netG.apply(weights_init)
|
| 44 |
+
return netG
|
| 45 |
+
|
| 46 |
+
def define_D(input_nc, ndf, n_layers_D, norm='instance', use_sigmoid=False, num_D=1, getIntermFeat=False, gpu_ids=[]):
|
| 47 |
+
norm_layer = get_norm_layer(norm_type=norm)
|
| 48 |
+
netD = MultiscaleDiscriminator(input_nc, ndf, n_layers_D, norm_layer, use_sigmoid, num_D, getIntermFeat)
|
| 49 |
+
print(netD)
|
| 50 |
+
if len(gpu_ids) > 0:
|
| 51 |
+
assert(torch.cuda.is_available())
|
| 52 |
+
netD.cuda(gpu_ids[0])
|
| 53 |
+
netD.apply(weights_init)
|
| 54 |
+
return netD
|
| 55 |
+
|
| 56 |
+
def print_network(net):
|
| 57 |
+
if isinstance(net, list):
|
| 58 |
+
net = net[0]
|
| 59 |
+
num_params = 0
|
| 60 |
+
for param in net.parameters():
|
| 61 |
+
num_params += param.numel()
|
| 62 |
+
print(net)
|
| 63 |
+
print('Total number of parameters in millions: ',num_params/1000000)
|
| 64 |
+
|
| 65 |
+
##############################################################################
|
| 66 |
+
# Losses
|
| 67 |
+
##############################################################################
|
| 68 |
+
class GANLoss(nn.Module):
|
| 69 |
+
def __init__(self, use_lsgan=True, target_real_label=1.0, target_fake_label=0.0,
|
| 70 |
+
tensor=torch.FloatTensor):
|
| 71 |
+
super(GANLoss, self).__init__()
|
| 72 |
+
self.real_label = target_real_label
|
| 73 |
+
self.fake_label = target_fake_label
|
| 74 |
+
self.real_label_var = None
|
| 75 |
+
self.fake_label_var = None
|
| 76 |
+
self.Tensor = tensor
|
| 77 |
+
if use_lsgan:
|
| 78 |
+
self.loss = nn.MSELoss()
|
| 79 |
+
else:
|
| 80 |
+
self.loss = nn.BCELoss()
|
| 81 |
+
|
| 82 |
+
def get_target_tensor(self, input, target_is_real):
|
| 83 |
+
target_tensor = None
|
| 84 |
+
if target_is_real:
|
| 85 |
+
create_label = ((self.real_label_var is None) or
|
| 86 |
+
(self.real_label_var.numel() != input.numel()))
|
| 87 |
+
if create_label:
|
| 88 |
+
real_tensor = self.Tensor(input.size()).fill_(self.real_label)
|
| 89 |
+
self.real_label_var = Variable(real_tensor, requires_grad=False)
|
| 90 |
+
target_tensor = self.real_label_var
|
| 91 |
+
else:
|
| 92 |
+
create_label = ((self.fake_label_var is None) or
|
| 93 |
+
(self.fake_label_var.numel() != input.numel()))
|
| 94 |
+
if create_label:
|
| 95 |
+
fake_tensor = self.Tensor(input.size()).fill_(self.fake_label)
|
| 96 |
+
self.fake_label_var = Variable(fake_tensor, requires_grad=False)
|
| 97 |
+
target_tensor = self.fake_label_var
|
| 98 |
+
return target_tensor
|
| 99 |
+
|
| 100 |
+
def __call__(self, input, target_is_real):
|
| 101 |
+
if isinstance(input[0], list):
|
| 102 |
+
loss = 0
|
| 103 |
+
for input_i in input:
|
| 104 |
+
pred = input_i[-1]
|
| 105 |
+
target_tensor = self.get_target_tensor(pred, target_is_real)
|
| 106 |
+
loss += self.loss(pred, target_tensor)
|
| 107 |
+
return loss
|
| 108 |
+
else:
|
| 109 |
+
target_tensor = self.get_target_tensor(input[-1], target_is_real)
|
| 110 |
+
return self.loss(input[-1], target_tensor)
|
| 111 |
+
|
| 112 |
+
class VGGLoss(nn.Module):
|
| 113 |
+
def __init__(self, gpu_ids):
|
| 114 |
+
super(VGGLoss, self).__init__()
|
| 115 |
+
self.vgg = Vgg19().cuda()
|
| 116 |
+
self.criterion = nn.L1Loss()
|
| 117 |
+
self.weights = [1.0/32, 1.0/16, 1.0/8, 1.0/4, 1.0]
|
| 118 |
+
|
| 119 |
+
def forward(self, x, y):
|
| 120 |
+
x_vgg, y_vgg = self.vgg(x), self.vgg(y)
|
| 121 |
+
loss = 0
|
| 122 |
+
for i in range(len(x_vgg)):
|
| 123 |
+
loss += self.weights[i] * self.criterion(x_vgg[i], y_vgg[i].detach())
|
| 124 |
+
return loss
|
| 125 |
+
|
| 126 |
+
##############################################################################
|
| 127 |
+
# Generator
|
| 128 |
+
##############################################################################
|
| 129 |
+
class LocalEnhancer(nn.Module):
|
| 130 |
+
def __init__(self, input_nc, output_nc, ngf=32, n_downsample_global=3, n_blocks_global=9,
|
| 131 |
+
n_local_enhancers=1, n_blocks_local=3, norm_layer=nn.BatchNorm2d, padding_type='reflect'):
|
| 132 |
+
super(LocalEnhancer, self).__init__()
|
| 133 |
+
self.n_local_enhancers = n_local_enhancers
|
| 134 |
+
|
| 135 |
+
###### global generator model #####
|
| 136 |
+
ngf_global = ngf * (2**n_local_enhancers)
|
| 137 |
+
model_global = GlobalGenerator(input_nc, output_nc, ngf_global, n_downsample_global, n_blocks_global, norm_layer).model
|
| 138 |
+
model_global = [model_global[i] for i in range(len(model_global)-3)] # get rid of final convolution layers
|
| 139 |
+
self.model = nn.Sequential(*model_global)
|
| 140 |
+
|
| 141 |
+
###### local enhancer layers #####
|
| 142 |
+
for n in range(1, n_local_enhancers+1):
|
| 143 |
+
### downsample
|
| 144 |
+
ngf_global = ngf * (2**(n_local_enhancers-n))
|
| 145 |
+
model_downsample = [nn.ReflectionPad2d(3), nn.Conv2d(input_nc, ngf_global, kernel_size=7, padding=0),
|
| 146 |
+
norm_layer(ngf_global), nn.ReLU(True),
|
| 147 |
+
nn.Conv2d(ngf_global, ngf_global * 2, kernel_size=3, stride=2, padding=1),
|
| 148 |
+
norm_layer(ngf_global * 2), nn.ReLU(True)]
|
| 149 |
+
### residual blocks
|
| 150 |
+
model_upsample = []
|
| 151 |
+
for i in range(n_blocks_local):
|
| 152 |
+
model_upsample += [ResnetBlock(ngf_global * 2, padding_type=padding_type, norm_layer=norm_layer)]
|
| 153 |
+
|
| 154 |
+
### upsample
|
| 155 |
+
model_upsample += [nn.ConvTranspose2d(ngf_global * 2, ngf_global, kernel_size=3, stride=2, padding=1, output_padding=1),
|
| 156 |
+
norm_layer(ngf_global), nn.ReLU(True)]
|
| 157 |
+
|
| 158 |
+
### final convolution
|
| 159 |
+
if n == n_local_enhancers:
|
| 160 |
+
model_upsample += [nn.ReflectionPad2d(3), nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0), nn.Tanh()]
|
| 161 |
+
|
| 162 |
+
setattr(self, 'model'+str(n)+'_1', nn.Sequential(*model_downsample))
|
| 163 |
+
setattr(self, 'model'+str(n)+'_2', nn.Sequential(*model_upsample))
|
| 164 |
+
|
| 165 |
+
self.downsample = nn.AvgPool2d(3, stride=2, padding=[1, 1], count_include_pad=False)
|
| 166 |
+
|
| 167 |
+
def forward(self, input):
|
| 168 |
+
### create input pyramid
|
| 169 |
+
input_downsampled = [input]
|
| 170 |
+
for i in range(self.n_local_enhancers):
|
| 171 |
+
input_downsampled.append(self.downsample(input_downsampled[-1]))
|
| 172 |
+
|
| 173 |
+
### output at coarest level
|
| 174 |
+
output_prev = self.model(input_downsampled[-1])
|
| 175 |
+
### build up one layer at a time
|
| 176 |
+
for n_local_enhancers in range(1, self.n_local_enhancers+1):
|
| 177 |
+
model_downsample = getattr(self, 'model'+str(n_local_enhancers)+'_1')
|
| 178 |
+
model_upsample = getattr(self, 'model'+str(n_local_enhancers)+'_2')
|
| 179 |
+
input_i = input_downsampled[self.n_local_enhancers-n_local_enhancers]
|
| 180 |
+
output_prev = model_upsample(model_downsample(input_i) + output_prev)
|
| 181 |
+
return output_prev
|
| 182 |
+
|
| 183 |
+
class GlobalGenerator(nn.Module):
|
| 184 |
+
def __init__(self, input_nc, output_nc, ngf=64, n_downsampling=3, n_blocks=9, norm_layer=nn.BatchNorm2d,
|
| 185 |
+
padding_type='reflect'):
|
| 186 |
+
assert(n_blocks >= 0)
|
| 187 |
+
super(GlobalGenerator, self).__init__()
|
| 188 |
+
activation = nn.ReLU(True)
|
| 189 |
+
|
| 190 |
+
model = [nn.ReflectionPad2d(3), nn.Conv2d(input_nc, ngf, kernel_size=7, padding=0), norm_layer(ngf), activation]
|
| 191 |
+
### downsample
|
| 192 |
+
for i in range(n_downsampling):
|
| 193 |
+
mult = 2**i
|
| 194 |
+
model += [nn.Conv2d(ngf * mult, ngf * mult * 2, kernel_size=3, stride=2, padding=1),
|
| 195 |
+
norm_layer(ngf * mult * 2), activation]
|
| 196 |
+
|
| 197 |
+
### resnet blocks
|
| 198 |
+
mult = 2**n_downsampling
|
| 199 |
+
for i in range(n_blocks):
|
| 200 |
+
model += [ResnetBlock(ngf * mult, padding_type=padding_type, activation=activation, norm_layer=norm_layer)]
|
| 201 |
+
|
| 202 |
+
### upsample
|
| 203 |
+
for i in range(n_downsampling):
|
| 204 |
+
mult = 2**(n_downsampling - i)
|
| 205 |
+
model += [nn.ConvTranspose2d(ngf * mult, int(ngf * mult / 2), kernel_size=3, stride=2, padding=1, output_padding=1),
|
| 206 |
+
norm_layer(int(ngf * mult / 2)), activation]
|
| 207 |
+
model += [nn.ReflectionPad2d(3), nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0), nn.Tanh()]
|
| 208 |
+
self.model = nn.Sequential(*model)
|
| 209 |
+
|
| 210 |
+
def forward(self, input):
|
| 211 |
+
return self.model(input)
|
| 212 |
+
|
| 213 |
+
# Define a resnet block
|
| 214 |
+
class ResnetBlock(nn.Module):
|
| 215 |
+
def __init__(self, dim, padding_type, norm_layer, activation=nn.ReLU(True), use_dropout=False):
|
| 216 |
+
super(ResnetBlock, self).__init__()
|
| 217 |
+
self.conv_block = self.build_conv_block(dim, padding_type, norm_layer, activation, use_dropout)
|
| 218 |
+
|
| 219 |
+
def build_conv_block(self, dim, padding_type, norm_layer, activation, use_dropout):
|
| 220 |
+
conv_block = []
|
| 221 |
+
p = 0
|
| 222 |
+
if padding_type == 'reflect':
|
| 223 |
+
conv_block += [nn.ReflectionPad2d(1)]
|
| 224 |
+
elif padding_type == 'replicate':
|
| 225 |
+
conv_block += [nn.ReplicationPad2d(1)]
|
| 226 |
+
elif padding_type == 'zero':
|
| 227 |
+
p = 1
|
| 228 |
+
else:
|
| 229 |
+
raise NotImplementedError('padding [%s] is not implemented' % padding_type)
|
| 230 |
+
|
| 231 |
+
conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p),
|
| 232 |
+
norm_layer(dim),
|
| 233 |
+
activation]
|
| 234 |
+
if use_dropout:
|
| 235 |
+
conv_block += [nn.Dropout(0.5)]
|
| 236 |
+
|
| 237 |
+
p = 0
|
| 238 |
+
if padding_type == 'reflect':
|
| 239 |
+
conv_block += [nn.ReflectionPad2d(1)]
|
| 240 |
+
elif padding_type == 'replicate':
|
| 241 |
+
conv_block += [nn.ReplicationPad2d(1)]
|
| 242 |
+
elif padding_type == 'zero':
|
| 243 |
+
p = 1
|
| 244 |
+
else:
|
| 245 |
+
raise NotImplementedError('padding [%s] is not implemented' % padding_type)
|
| 246 |
+
conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p),
|
| 247 |
+
norm_layer(dim)]
|
| 248 |
+
|
| 249 |
+
return nn.Sequential(*conv_block)
|
| 250 |
+
|
| 251 |
+
def forward(self, x):
|
| 252 |
+
out = x + self.conv_block(x)
|
| 253 |
+
return out
|
| 254 |
+
|
| 255 |
+
class Encoder(nn.Module):
|
| 256 |
+
def __init__(self, input_nc, output_nc, ngf=32, n_downsampling=4, norm_layer=nn.BatchNorm2d):
|
| 257 |
+
super(Encoder, self).__init__()
|
| 258 |
+
self.output_nc = output_nc
|
| 259 |
+
|
| 260 |
+
model = [nn.ReflectionPad2d(3), nn.Conv2d(input_nc, ngf, kernel_size=7, padding=0),
|
| 261 |
+
norm_layer(ngf), nn.ReLU(True)]
|
| 262 |
+
### downsample
|
| 263 |
+
for i in range(n_downsampling):
|
| 264 |
+
mult = 2**i
|
| 265 |
+
model += [nn.Conv2d(ngf * mult, ngf * mult * 2, kernel_size=3, stride=2, padding=1),
|
| 266 |
+
norm_layer(ngf * mult * 2), nn.ReLU(True)]
|
| 267 |
+
|
| 268 |
+
### upsample
|
| 269 |
+
for i in range(n_downsampling):
|
| 270 |
+
mult = 2**(n_downsampling - i)
|
| 271 |
+
model += [nn.ConvTranspose2d(ngf * mult, int(ngf * mult / 2), kernel_size=3, stride=2, padding=1, output_padding=1),
|
| 272 |
+
norm_layer(int(ngf * mult / 2)), nn.ReLU(True)]
|
| 273 |
+
|
| 274 |
+
model += [nn.ReflectionPad2d(3), nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0), nn.Tanh()]
|
| 275 |
+
self.model = nn.Sequential(*model)
|
| 276 |
+
|
| 277 |
+
def forward(self, input, inst):
|
| 278 |
+
outputs = self.model(input)
|
| 279 |
+
|
| 280 |
+
# instance-wise average pooling
|
| 281 |
+
outputs_mean = outputs.clone()
|
| 282 |
+
inst_list = np.unique(inst.cpu().numpy().astype(int))
|
| 283 |
+
for i in inst_list:
|
| 284 |
+
for b in range(input.size()[0]):
|
| 285 |
+
indices = (inst[b:b+1] == int(i)).nonzero() # n x 4
|
| 286 |
+
for j in range(self.output_nc):
|
| 287 |
+
output_ins = outputs[indices[:,0] + b, indices[:,1] + j, indices[:,2], indices[:,3]]
|
| 288 |
+
mean_feat = torch.mean(output_ins).expand_as(output_ins)
|
| 289 |
+
outputs_mean[indices[:,0] + b, indices[:,1] + j, indices[:,2], indices[:,3]] = mean_feat
|
| 290 |
+
return outputs_mean
|
| 291 |
+
|
| 292 |
+
class MultiscaleDiscriminator(nn.Module):
|
| 293 |
+
def __init__(self, input_nc, ndf=64, n_layers=3, norm_layer=nn.BatchNorm2d,
|
| 294 |
+
use_sigmoid=False, num_D=3, getIntermFeat=False):
|
| 295 |
+
super(MultiscaleDiscriminator, self).__init__()
|
| 296 |
+
self.num_D = num_D
|
| 297 |
+
self.n_layers = n_layers
|
| 298 |
+
self.getIntermFeat = getIntermFeat
|
| 299 |
+
|
| 300 |
+
for i in range(num_D):
|
| 301 |
+
netD = NLayerDiscriminator(input_nc, ndf, n_layers, norm_layer, use_sigmoid, getIntermFeat)
|
| 302 |
+
if getIntermFeat:
|
| 303 |
+
for j in range(n_layers+2):
|
| 304 |
+
setattr(self, 'scale'+str(i)+'_layer'+str(j), getattr(netD, 'model'+str(j)))
|
| 305 |
+
else:
|
| 306 |
+
setattr(self, 'layer'+str(i), netD.model)
|
| 307 |
+
|
| 308 |
+
self.downsample = nn.AvgPool2d(3, stride=2, padding=[1, 1], count_include_pad=False)
|
| 309 |
+
|
| 310 |
+
def singleD_forward(self, model, input):
|
| 311 |
+
if self.getIntermFeat:
|
| 312 |
+
result = [input]
|
| 313 |
+
for i in range(len(model)):
|
| 314 |
+
result.append(model[i](result[-1]))
|
| 315 |
+
return result[1:]
|
| 316 |
+
else:
|
| 317 |
+
return [model(input)]
|
| 318 |
+
|
| 319 |
+
def forward(self, input):
|
| 320 |
+
num_D = self.num_D
|
| 321 |
+
result = []
|
| 322 |
+
input_downsampled = input
|
| 323 |
+
for i in range(num_D):
|
| 324 |
+
if self.getIntermFeat:
|
| 325 |
+
model = [getattr(self, 'scale'+str(num_D-1-i)+'_layer'+str(j)) for j in range(self.n_layers+2)]
|
| 326 |
+
else:
|
| 327 |
+
model = getattr(self, 'layer'+str(num_D-1-i))
|
| 328 |
+
result.append(self.singleD_forward(model, input_downsampled))
|
| 329 |
+
if i != (num_D-1):
|
| 330 |
+
input_downsampled = self.downsample(input_downsampled)
|
| 331 |
+
return result
|
| 332 |
+
|
| 333 |
+
# Defines the PatchGAN discriminator with the specified arguments.
|
| 334 |
+
class NLayerDiscriminator(nn.Module):
|
| 335 |
+
def __init__(self, input_nc, ndf=64, n_layers=3, norm_layer=nn.BatchNorm2d, use_sigmoid=False, getIntermFeat=False):
|
| 336 |
+
super(NLayerDiscriminator, self).__init__()
|
| 337 |
+
self.getIntermFeat = getIntermFeat
|
| 338 |
+
self.n_layers = n_layers
|
| 339 |
+
|
| 340 |
+
kw = 4
|
| 341 |
+
padw = int(np.ceil((kw-1.0)/2))
|
| 342 |
+
sequence = [[nn.Conv2d(input_nc, ndf, kernel_size=kw, stride=2, padding=padw), nn.LeakyReLU(0.2, True)]]
|
| 343 |
+
|
| 344 |
+
nf = ndf
|
| 345 |
+
for n in range(1, n_layers):
|
| 346 |
+
nf_prev = nf
|
| 347 |
+
nf = min(nf * 2, 512)
|
| 348 |
+
sequence += [[
|
| 349 |
+
nn.Conv2d(nf_prev, nf, kernel_size=kw, stride=2, padding=padw),
|
| 350 |
+
norm_layer(nf), nn.LeakyReLU(0.2, True)
|
| 351 |
+
]]
|
| 352 |
+
|
| 353 |
+
nf_prev = nf
|
| 354 |
+
nf = min(nf * 2, 512)
|
| 355 |
+
sequence += [[
|
| 356 |
+
nn.Conv2d(nf_prev, nf, kernel_size=kw, stride=1, padding=padw),
|
| 357 |
+
norm_layer(nf),
|
| 358 |
+
nn.LeakyReLU(0.2, True)
|
| 359 |
+
]]
|
| 360 |
+
|
| 361 |
+
sequence += [[nn.Conv2d(nf, 1, kernel_size=kw, stride=1, padding=padw)]]
|
| 362 |
+
|
| 363 |
+
if use_sigmoid:
|
| 364 |
+
sequence += [[nn.Sigmoid()]]
|
| 365 |
+
|
| 366 |
+
if getIntermFeat:
|
| 367 |
+
for n in range(len(sequence)):
|
| 368 |
+
setattr(self, 'model'+str(n), nn.Sequential(*sequence[n]))
|
| 369 |
+
else:
|
| 370 |
+
sequence_stream = []
|
| 371 |
+
for n in range(len(sequence)):
|
| 372 |
+
sequence_stream += sequence[n]
|
| 373 |
+
self.model = nn.Sequential(*sequence_stream)
|
| 374 |
+
|
| 375 |
+
def forward(self, input):
|
| 376 |
+
if self.getIntermFeat:
|
| 377 |
+
res = [input]
|
| 378 |
+
for n in range(self.n_layers+2):
|
| 379 |
+
model = getattr(self, 'model'+str(n))
|
| 380 |
+
res.append(model(res[-1]))
|
| 381 |
+
return res[1:]
|
| 382 |
+
else:
|
| 383 |
+
return self.model(input)
|
| 384 |
+
|
| 385 |
+
from torchvision import models
|
| 386 |
+
class Vgg19(torch.nn.Module):
|
| 387 |
+
def __init__(self, requires_grad=False):
|
| 388 |
+
super(Vgg19, self).__init__()
|
| 389 |
+
vgg_pretrained_features = models.vgg19(pretrained=True).features
|
| 390 |
+
self.slice1 = torch.nn.Sequential()
|
| 391 |
+
self.slice2 = torch.nn.Sequential()
|
| 392 |
+
self.slice3 = torch.nn.Sequential()
|
| 393 |
+
self.slice4 = torch.nn.Sequential()
|
| 394 |
+
self.slice5 = torch.nn.Sequential()
|
| 395 |
+
for x in range(2):
|
| 396 |
+
self.slice1.add_module(str(x), vgg_pretrained_features[x])
|
| 397 |
+
for x in range(2, 7):
|
| 398 |
+
self.slice2.add_module(str(x), vgg_pretrained_features[x])
|
| 399 |
+
for x in range(7, 12):
|
| 400 |
+
self.slice3.add_module(str(x), vgg_pretrained_features[x])
|
| 401 |
+
for x in range(12, 21):
|
| 402 |
+
self.slice4.add_module(str(x), vgg_pretrained_features[x])
|
| 403 |
+
for x in range(21, 30):
|
| 404 |
+
self.slice5.add_module(str(x), vgg_pretrained_features[x])
|
| 405 |
+
if not requires_grad:
|
| 406 |
+
for param in self.parameters():
|
| 407 |
+
param.requires_grad = False
|
| 408 |
+
|
| 409 |
+
def forward(self, X):
|
| 410 |
+
h_relu1 = self.slice1(X)
|
| 411 |
+
h_relu2 = self.slice2(h_relu1)
|
| 412 |
+
h_relu3 = self.slice3(h_relu2)
|
| 413 |
+
h_relu4 = self.slice4(h_relu3)
|
| 414 |
+
h_relu5 = self.slice5(h_relu4)
|
| 415 |
+
out = [h_relu1, h_relu2, h_relu3, h_relu4, h_relu5]
|
| 416 |
+
return out
|
models/pix2pixHD_model.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import torch
|
| 3 |
+
import os
|
| 4 |
+
from torch.autograd import Variable
|
| 5 |
+
from util.image_pool import ImagePool
|
| 6 |
+
from .base_model import BaseModel
|
| 7 |
+
from . import networks
|
| 8 |
+
|
| 9 |
+
class Pix2PixHDModel(BaseModel):
|
| 10 |
+
def name(self):
|
| 11 |
+
return 'Pix2PixHDModel'
|
| 12 |
+
|
| 13 |
+
def init_loss_filter(self, use_gan_feat_loss, use_vgg_loss):
|
| 14 |
+
flags = (True, use_gan_feat_loss, use_vgg_loss, True, True)
|
| 15 |
+
def loss_filter(g_gan, g_gan_feat, g_vgg, d_real, d_fake):
|
| 16 |
+
return [l for (l,f) in zip((g_gan,g_gan_feat,g_vgg,d_real,d_fake),flags) if f]
|
| 17 |
+
return loss_filter
|
| 18 |
+
|
| 19 |
+
def initialize(self, opt):
|
| 20 |
+
BaseModel.initialize(self, opt)
|
| 21 |
+
if opt.resize_or_crop != 'none' or not opt.isTrain: # when training at full res this causes OOM
|
| 22 |
+
torch.backends.cudnn.benchmark = True
|
| 23 |
+
self.isTrain = opt.isTrain
|
| 24 |
+
self.use_features = opt.instance_feat or opt.label_feat
|
| 25 |
+
self.gen_features = self.use_features and not self.opt.load_features
|
| 26 |
+
input_nc = opt.label_nc if opt.label_nc != 0 else opt.input_nc
|
| 27 |
+
|
| 28 |
+
##### define networks
|
| 29 |
+
# Generator network
|
| 30 |
+
netG_input_nc = input_nc
|
| 31 |
+
if not opt.no_instance:
|
| 32 |
+
netG_input_nc += 1
|
| 33 |
+
if self.use_features:
|
| 34 |
+
netG_input_nc += opt.feat_num
|
| 35 |
+
self.netG = networks.define_G(netG_input_nc, opt.output_nc, opt.ngf, opt.netG,
|
| 36 |
+
opt.n_downsample_global, opt.n_blocks_global, opt.n_local_enhancers,
|
| 37 |
+
opt.n_blocks_local, opt.norm, gpu_ids=self.gpu_ids)
|
| 38 |
+
networks.print_network(self.netG)
|
| 39 |
+
|
| 40 |
+
# Discriminator network
|
| 41 |
+
if self.isTrain:
|
| 42 |
+
use_sigmoid = opt.no_lsgan
|
| 43 |
+
netD_input_nc = input_nc + opt.output_nc
|
| 44 |
+
if not opt.no_instance:
|
| 45 |
+
netD_input_nc += 1
|
| 46 |
+
self.netD = networks.define_D(netD_input_nc, opt.ndf, opt.n_layers_D, opt.norm, use_sigmoid,
|
| 47 |
+
opt.num_D, not opt.no_ganFeat_loss, gpu_ids=self.gpu_ids)
|
| 48 |
+
networks.print_network(self.netD)
|
| 49 |
+
|
| 50 |
+
### Encoder network
|
| 51 |
+
if self.gen_features:
|
| 52 |
+
self.netE = networks.define_G(opt.output_nc, opt.feat_num, opt.nef, 'encoder',
|
| 53 |
+
opt.n_downsample_E, norm=opt.norm, gpu_ids=self.gpu_ids)
|
| 54 |
+
if self.opt.verbose:
|
| 55 |
+
print('---------- Networks initialized -------------')
|
| 56 |
+
|
| 57 |
+
# load networks
|
| 58 |
+
if not self.isTrain or opt.continue_train or opt.load_pretrain:
|
| 59 |
+
pretrained_path = '' if not self.isTrain else opt.load_pretrain
|
| 60 |
+
self.load_network(self.netG, 'G', opt.which_epoch, pretrained_path)
|
| 61 |
+
if self.isTrain:
|
| 62 |
+
self.load_network(self.netD, 'D', opt.which_epoch, pretrained_path)
|
| 63 |
+
if self.gen_features:
|
| 64 |
+
self.load_network(self.netE, 'E', opt.which_epoch, pretrained_path)
|
| 65 |
+
|
| 66 |
+
# set loss functions and optimizers
|
| 67 |
+
if self.isTrain:
|
| 68 |
+
if opt.pool_size > 0 and (len(self.gpu_ids)) > 1:
|
| 69 |
+
raise NotImplementedError("Fake Pool Not Implemented for MultiGPU")
|
| 70 |
+
self.fake_pool = ImagePool(opt.pool_size)
|
| 71 |
+
self.old_lr = opt.lr
|
| 72 |
+
|
| 73 |
+
# define loss functions
|
| 74 |
+
self.loss_filter = self.init_loss_filter(not opt.no_ganFeat_loss, not opt.no_vgg_loss)
|
| 75 |
+
|
| 76 |
+
self.criterionGAN = networks.GANLoss(use_lsgan=not opt.no_lsgan, tensor=self.Tensor)
|
| 77 |
+
self.criterionFeat = torch.nn.L1Loss()
|
| 78 |
+
if not opt.no_vgg_loss:
|
| 79 |
+
self.criterionVGG = networks.VGGLoss(self.gpu_ids)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
# Names so we can breakout loss
|
| 83 |
+
self.loss_names = self.loss_filter('G_GAN','G_GAN_Feat','G_VGG','D_real', 'D_fake')
|
| 84 |
+
|
| 85 |
+
# initialize optimizers
|
| 86 |
+
# optimizer G
|
| 87 |
+
if opt.niter_fix_global > 0:
|
| 88 |
+
import sys
|
| 89 |
+
if sys.version_info >= (3,0):
|
| 90 |
+
finetune_list = set()
|
| 91 |
+
else:
|
| 92 |
+
from sets import Set
|
| 93 |
+
finetune_list = Set()
|
| 94 |
+
|
| 95 |
+
params_dict = dict(self.netG.named_parameters())
|
| 96 |
+
params = []
|
| 97 |
+
for key, value in params_dict.items():
|
| 98 |
+
if key.startswith('model' + str(opt.n_local_enhancers)):
|
| 99 |
+
params += [value]
|
| 100 |
+
finetune_list.add(key.split('.')[0])
|
| 101 |
+
print('------------- Only training the local enhancer network (for %d epochs) ------------' % opt.niter_fix_global)
|
| 102 |
+
print('The layers that are finetuned are ', sorted(finetune_list))
|
| 103 |
+
else:
|
| 104 |
+
params = list(self.netG.parameters())
|
| 105 |
+
if self.gen_features:
|
| 106 |
+
params += list(self.netE.parameters())
|
| 107 |
+
self.optimizer_G = torch.optim.Adam(params, lr=opt.lr, betas=(opt.beta1, 0.999))
|
| 108 |
+
|
| 109 |
+
# optimizer D
|
| 110 |
+
params = list(self.netD.parameters())
|
| 111 |
+
self.optimizer_D = torch.optim.Adam(params, lr=opt.lr, betas=(opt.beta1, 0.999))
|
| 112 |
+
|
| 113 |
+
def encode_input(self, label_map, inst_map=None, real_image=None, feat_map=None, infer=False):
|
| 114 |
+
if self.opt.label_nc == 0:
|
| 115 |
+
input_label = label_map.data.cuda()
|
| 116 |
+
else:
|
| 117 |
+
# create one-hot vector for label map
|
| 118 |
+
size = label_map.size()
|
| 119 |
+
oneHot_size = (size[0], self.opt.label_nc, size[2], size[3])
|
| 120 |
+
input_label = torch.cuda.FloatTensor(torch.Size(oneHot_size)).zero_()
|
| 121 |
+
input_label = input_label.scatter_(1, label_map.data.long().cuda(), 1.0)
|
| 122 |
+
if self.opt.data_type == 16:
|
| 123 |
+
input_label = input_label.half()
|
| 124 |
+
|
| 125 |
+
# get edges from instance map
|
| 126 |
+
if not self.opt.no_instance:
|
| 127 |
+
inst_map = inst_map.data.cuda()
|
| 128 |
+
edge_map = self.get_edges(inst_map)
|
| 129 |
+
input_label = torch.cat((input_label, edge_map), dim=1)
|
| 130 |
+
input_label = Variable(input_label, volatile=infer)
|
| 131 |
+
|
| 132 |
+
# real images for training
|
| 133 |
+
if real_image is not None:
|
| 134 |
+
real_image = Variable(real_image.data.cuda())
|
| 135 |
+
|
| 136 |
+
# instance map for feature encoding
|
| 137 |
+
if self.use_features:
|
| 138 |
+
# get precomputed feature maps
|
| 139 |
+
if self.opt.load_features:
|
| 140 |
+
feat_map = Variable(feat_map.data.cuda())
|
| 141 |
+
if self.opt.label_feat:
|
| 142 |
+
inst_map = label_map.cuda()
|
| 143 |
+
|
| 144 |
+
return input_label, inst_map, real_image, feat_map
|
| 145 |
+
|
| 146 |
+
def discriminate(self, input_label, test_image, use_pool=False):
|
| 147 |
+
input_concat = torch.cat((input_label, test_image.detach()), dim=1)
|
| 148 |
+
if use_pool:
|
| 149 |
+
fake_query = self.fake_pool.query(input_concat)
|
| 150 |
+
return self.netD.forward(fake_query)
|
| 151 |
+
else:
|
| 152 |
+
return self.netD.forward(input_concat)
|
| 153 |
+
|
| 154 |
+
def forward(self, label, inst, image, feat, infer=False):
|
| 155 |
+
# Encode Inputs
|
| 156 |
+
input_label, inst_map, real_image, feat_map = self.encode_input(label, inst, image, feat)
|
| 157 |
+
|
| 158 |
+
# Fake Generation
|
| 159 |
+
if self.use_features:
|
| 160 |
+
if not self.opt.load_features:
|
| 161 |
+
feat_map = self.netE.forward(real_image, inst_map)
|
| 162 |
+
input_concat = torch.cat((input_label, feat_map), dim=1)
|
| 163 |
+
else:
|
| 164 |
+
input_concat = input_label
|
| 165 |
+
fake_image = self.netG.forward(input_concat)
|
| 166 |
+
|
| 167 |
+
# Fake Detection and Loss
|
| 168 |
+
pred_fake_pool = self.discriminate(input_label, fake_image, use_pool=True)
|
| 169 |
+
loss_D_fake = self.criterionGAN(pred_fake_pool, False)
|
| 170 |
+
|
| 171 |
+
# Real Detection and Loss
|
| 172 |
+
pred_real = self.discriminate(input_label, real_image)
|
| 173 |
+
loss_D_real = self.criterionGAN(pred_real, True)
|
| 174 |
+
|
| 175 |
+
# GAN loss (Fake Passability Loss)
|
| 176 |
+
pred_fake = self.netD.forward(torch.cat((input_label, fake_image), dim=1))
|
| 177 |
+
loss_G_GAN = self.criterionGAN(pred_fake, True)
|
| 178 |
+
|
| 179 |
+
# GAN feature matching loss
|
| 180 |
+
loss_G_GAN_Feat = 0
|
| 181 |
+
if not self.opt.no_ganFeat_loss:
|
| 182 |
+
feat_weights = 4.0 / (self.opt.n_layers_D + 1)
|
| 183 |
+
D_weights = 1.0 / self.opt.num_D
|
| 184 |
+
for i in range(self.opt.num_D):
|
| 185 |
+
for j in range(len(pred_fake[i])-1):
|
| 186 |
+
loss_G_GAN_Feat += D_weights * feat_weights * \
|
| 187 |
+
self.criterionFeat(pred_fake[i][j], pred_real[i][j].detach()) * self.opt.lambda_feat
|
| 188 |
+
|
| 189 |
+
# VGG feature matching loss
|
| 190 |
+
loss_G_VGG = 0
|
| 191 |
+
if not self.opt.no_vgg_loss:
|
| 192 |
+
loss_G_VGG = self.criterionVGG(fake_image, real_image) * self.opt.lambda_feat
|
| 193 |
+
|
| 194 |
+
# Only return the fake_B image if necessary to save BW
|
| 195 |
+
return [ self.loss_filter( loss_G_GAN, loss_G_GAN_Feat, loss_G_VGG, loss_D_real, loss_D_fake ), None if not infer else fake_image ]
|
| 196 |
+
|
| 197 |
+
def inference(self, label, inst, image=None):
|
| 198 |
+
# Encode Inputs
|
| 199 |
+
image = Variable(image) if image is not None else None
|
| 200 |
+
input_label, inst_map, real_image, _ = self.encode_input(Variable(label), Variable(inst), image, infer=True)
|
| 201 |
+
|
| 202 |
+
# Fake Generation
|
| 203 |
+
if self.use_features:
|
| 204 |
+
if self.opt.use_encoded_image:
|
| 205 |
+
# encode the real image to get feature map
|
| 206 |
+
feat_map = self.netE.forward(real_image, inst_map)
|
| 207 |
+
else:
|
| 208 |
+
# sample clusters from precomputed features
|
| 209 |
+
feat_map = self.sample_features(inst_map)
|
| 210 |
+
input_concat = torch.cat((input_label, feat_map), dim=1)
|
| 211 |
+
else:
|
| 212 |
+
input_concat = input_label
|
| 213 |
+
|
| 214 |
+
if torch.__version__.startswith('0.4'):
|
| 215 |
+
with torch.no_grad():
|
| 216 |
+
fake_image = self.netG.forward(input_concat)
|
| 217 |
+
else:
|
| 218 |
+
fake_image = self.netG.forward(input_concat)
|
| 219 |
+
return fake_image
|
| 220 |
+
|
| 221 |
+
def sample_features(self, inst):
|
| 222 |
+
# read precomputed feature clusters
|
| 223 |
+
cluster_path = os.path.join(self.opt.checkpoints_dir, self.opt.name, self.opt.cluster_path)
|
| 224 |
+
features_clustered = np.load(cluster_path, encoding='latin1').item()
|
| 225 |
+
|
| 226 |
+
# randomly sample from the feature clusters
|
| 227 |
+
inst_np = inst.cpu().numpy().astype(int)
|
| 228 |
+
feat_map = self.Tensor(inst.size()[0], self.opt.feat_num, inst.size()[2], inst.size()[3])
|
| 229 |
+
for i in np.unique(inst_np):
|
| 230 |
+
label = i if i < 1000 else i//1000
|
| 231 |
+
if label in features_clustered:
|
| 232 |
+
feat = features_clustered[label]
|
| 233 |
+
cluster_idx = np.random.randint(0, feat.shape[0])
|
| 234 |
+
|
| 235 |
+
idx = (inst == int(i)).nonzero()
|
| 236 |
+
for k in range(self.opt.feat_num):
|
| 237 |
+
feat_map[idx[:,0], idx[:,1] + k, idx[:,2], idx[:,3]] = feat[cluster_idx, k]
|
| 238 |
+
if self.opt.data_type==16:
|
| 239 |
+
feat_map = feat_map.half()
|
| 240 |
+
return feat_map
|
| 241 |
+
|
| 242 |
+
def encode_features(self, image, inst):
|
| 243 |
+
image = Variable(image.cuda(), volatile=True)
|
| 244 |
+
feat_num = self.opt.feat_num
|
| 245 |
+
h, w = inst.size()[2], inst.size()[3]
|
| 246 |
+
block_num = 32
|
| 247 |
+
feat_map = self.netE.forward(image, inst.cuda())
|
| 248 |
+
inst_np = inst.cpu().numpy().astype(int)
|
| 249 |
+
feature = {}
|
| 250 |
+
for i in range(self.opt.label_nc):
|
| 251 |
+
feature[i] = np.zeros((0, feat_num+1))
|
| 252 |
+
for i in np.unique(inst_np):
|
| 253 |
+
label = i if i < 1000 else i//1000
|
| 254 |
+
idx = (inst == int(i)).nonzero()
|
| 255 |
+
num = idx.size()[0]
|
| 256 |
+
idx = idx[num//2,:]
|
| 257 |
+
val = np.zeros((1, feat_num+1))
|
| 258 |
+
for k in range(feat_num):
|
| 259 |
+
val[0, k] = feat_map[idx[0], idx[1] + k, idx[2], idx[3]].data[0]
|
| 260 |
+
val[0, feat_num] = float(num) / (h * w // block_num)
|
| 261 |
+
feature[label] = np.append(feature[label], val, axis=0)
|
| 262 |
+
return feature
|
| 263 |
+
|
| 264 |
+
def get_edges(self, t):
|
| 265 |
+
edge = torch.cuda.ByteTensor(t.size()).zero_()
|
| 266 |
+
edge[:,:,:,1:] = edge[:,:,:,1:] | (t[:,:,:,1:] != t[:,:,:,:-1])
|
| 267 |
+
edge[:,:,:,:-1] = edge[:,:,:,:-1] | (t[:,:,:,1:] != t[:,:,:,:-1])
|
| 268 |
+
edge[:,:,1:,:] = edge[:,:,1:,:] | (t[:,:,1:,:] != t[:,:,:-1,:])
|
| 269 |
+
edge[:,:,:-1,:] = edge[:,:,:-1,:] | (t[:,:,1:,:] != t[:,:,:-1,:])
|
| 270 |
+
if self.opt.data_type==16:
|
| 271 |
+
return edge.half()
|
| 272 |
+
else:
|
| 273 |
+
return edge.float()
|
| 274 |
+
|
| 275 |
+
def save(self, which_epoch):
|
| 276 |
+
self.save_network(self.netG, 'G', which_epoch, self.gpu_ids)
|
| 277 |
+
self.save_network(self.netD, 'D', which_epoch, self.gpu_ids)
|
| 278 |
+
if self.gen_features:
|
| 279 |
+
self.save_network(self.netE, 'E', which_epoch, self.gpu_ids)
|
| 280 |
+
|
| 281 |
+
def update_fixed_params(self):
|
| 282 |
+
# after fixing the global generator for a number of iterations, also start finetuning it
|
| 283 |
+
params = list(self.netG.parameters())
|
| 284 |
+
if self.gen_features:
|
| 285 |
+
params += list(self.netE.parameters())
|
| 286 |
+
self.optimizer_G = torch.optim.Adam(params, lr=self.opt.lr, betas=(self.opt.beta1, 0.999))
|
| 287 |
+
if self.opt.verbose:
|
| 288 |
+
print('------------ Now also finetuning global generator -----------')
|
| 289 |
+
|
| 290 |
+
def update_learning_rate(self):
|
| 291 |
+
lrd = self.opt.lr / self.opt.niter_decay
|
| 292 |
+
lr = self.old_lr - lrd
|
| 293 |
+
for param_group in self.optimizer_D.param_groups:
|
| 294 |
+
param_group['lr'] = lr
|
| 295 |
+
for param_group in self.optimizer_G.param_groups:
|
| 296 |
+
param_group['lr'] = lr
|
| 297 |
+
if self.opt.verbose:
|
| 298 |
+
print('update learning rate: %f -> %f' % (self.old_lr, lr))
|
| 299 |
+
self.old_lr = lr
|
| 300 |
+
|
| 301 |
+
class InferenceModel(Pix2PixHDModel):
|
| 302 |
+
def forward(self, inp):
|
| 303 |
+
label, inst = inp
|
| 304 |
+
return self.inference(label, inst)
|
| 305 |
+
|
| 306 |
+
|
models/ui_model.py
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch.autograd import Variable
|
| 3 |
+
from collections import OrderedDict
|
| 4 |
+
import numpy as np
|
| 5 |
+
import os
|
| 6 |
+
from PIL import Image
|
| 7 |
+
import util.util as util
|
| 8 |
+
from .base_model import BaseModel
|
| 9 |
+
from . import networks
|
| 10 |
+
|
| 11 |
+
class UIModel(BaseModel):
|
| 12 |
+
def name(self):
|
| 13 |
+
return 'UIModel'
|
| 14 |
+
|
| 15 |
+
def initialize(self, opt):
|
| 16 |
+
assert(not opt.isTrain)
|
| 17 |
+
BaseModel.initialize(self, opt)
|
| 18 |
+
self.use_features = opt.instance_feat or opt.label_feat
|
| 19 |
+
|
| 20 |
+
netG_input_nc = opt.label_nc
|
| 21 |
+
if not opt.no_instance:
|
| 22 |
+
netG_input_nc += 1
|
| 23 |
+
if self.use_features:
|
| 24 |
+
netG_input_nc += opt.feat_num
|
| 25 |
+
|
| 26 |
+
self.netG = networks.define_G(netG_input_nc, opt.output_nc, opt.ngf, opt.netG,
|
| 27 |
+
opt.n_downsample_global, opt.n_blocks_global, opt.n_local_enhancers,
|
| 28 |
+
opt.n_blocks_local, opt.norm, gpu_ids=self.gpu_ids)
|
| 29 |
+
self.load_network(self.netG, 'G', opt.which_epoch)
|
| 30 |
+
|
| 31 |
+
print('---------- Networks initialized -------------')
|
| 32 |
+
|
| 33 |
+
def toTensor(self, img, normalize=False):
|
| 34 |
+
tensor = torch.from_numpy(np.array(img, np.int32, copy=False))
|
| 35 |
+
tensor = tensor.view(1, img.size[1], img.size[0], len(img.mode))
|
| 36 |
+
tensor = tensor.transpose(1, 2).transpose(1, 3).contiguous()
|
| 37 |
+
if normalize:
|
| 38 |
+
return (tensor.float()/255.0 - 0.5) / 0.5
|
| 39 |
+
return tensor.float()
|
| 40 |
+
|
| 41 |
+
def load_image(self, label_path, inst_path, feat_path):
|
| 42 |
+
opt = self.opt
|
| 43 |
+
# read label map
|
| 44 |
+
label_img = Image.open(label_path)
|
| 45 |
+
if label_path.find('face') != -1:
|
| 46 |
+
label_img = label_img.convert('L')
|
| 47 |
+
ow, oh = label_img.size
|
| 48 |
+
w = opt.loadSize
|
| 49 |
+
h = int(w * oh / ow)
|
| 50 |
+
label_img = label_img.resize((w, h), Image.NEAREST)
|
| 51 |
+
label_map = self.toTensor(label_img)
|
| 52 |
+
|
| 53 |
+
# onehot vector input for label map
|
| 54 |
+
self.label_map = label_map.cuda()
|
| 55 |
+
oneHot_size = (1, opt.label_nc, h, w)
|
| 56 |
+
input_label = self.Tensor(torch.Size(oneHot_size)).zero_()
|
| 57 |
+
self.input_label = input_label.scatter_(1, label_map.long().cuda(), 1.0)
|
| 58 |
+
|
| 59 |
+
# read instance map
|
| 60 |
+
if not opt.no_instance:
|
| 61 |
+
inst_img = Image.open(inst_path)
|
| 62 |
+
inst_img = inst_img.resize((w, h), Image.NEAREST)
|
| 63 |
+
self.inst_map = self.toTensor(inst_img).cuda()
|
| 64 |
+
self.edge_map = self.get_edges(self.inst_map)
|
| 65 |
+
self.net_input = Variable(torch.cat((self.input_label, self.edge_map), dim=1), volatile=True)
|
| 66 |
+
else:
|
| 67 |
+
self.net_input = Variable(self.input_label, volatile=True)
|
| 68 |
+
|
| 69 |
+
self.features_clustered = np.load(feat_path).item()
|
| 70 |
+
self.object_map = self.inst_map if opt.instance_feat else self.label_map
|
| 71 |
+
|
| 72 |
+
object_np = self.object_map.cpu().numpy().astype(int)
|
| 73 |
+
self.feat_map = self.Tensor(1, opt.feat_num, h, w).zero_()
|
| 74 |
+
self.cluster_indices = np.zeros(self.opt.label_nc, np.uint8)
|
| 75 |
+
for i in np.unique(object_np):
|
| 76 |
+
label = i if i < 1000 else i//1000
|
| 77 |
+
if label in self.features_clustered:
|
| 78 |
+
feat = self.features_clustered[label]
|
| 79 |
+
np.random.seed(i+1)
|
| 80 |
+
cluster_idx = np.random.randint(0, feat.shape[0])
|
| 81 |
+
self.cluster_indices[label] = cluster_idx
|
| 82 |
+
idx = (self.object_map == i).nonzero()
|
| 83 |
+
self.set_features(idx, feat, cluster_idx)
|
| 84 |
+
|
| 85 |
+
self.net_input_original = self.net_input.clone()
|
| 86 |
+
self.label_map_original = self.label_map.clone()
|
| 87 |
+
self.feat_map_original = self.feat_map.clone()
|
| 88 |
+
if not opt.no_instance:
|
| 89 |
+
self.inst_map_original = self.inst_map.clone()
|
| 90 |
+
|
| 91 |
+
def reset(self):
|
| 92 |
+
self.net_input = self.net_input_prev = self.net_input_original.clone()
|
| 93 |
+
self.label_map = self.label_map_prev = self.label_map_original.clone()
|
| 94 |
+
self.feat_map = self.feat_map_prev = self.feat_map_original.clone()
|
| 95 |
+
if not self.opt.no_instance:
|
| 96 |
+
self.inst_map = self.inst_map_prev = self.inst_map_original.clone()
|
| 97 |
+
self.object_map = self.inst_map if self.opt.instance_feat else self.label_map
|
| 98 |
+
|
| 99 |
+
def undo(self):
|
| 100 |
+
self.net_input = self.net_input_prev
|
| 101 |
+
self.label_map = self.label_map_prev
|
| 102 |
+
self.feat_map = self.feat_map_prev
|
| 103 |
+
if not self.opt.no_instance:
|
| 104 |
+
self.inst_map = self.inst_map_prev
|
| 105 |
+
self.object_map = self.inst_map if self.opt.instance_feat else self.label_map
|
| 106 |
+
|
| 107 |
+
# get boundary map from instance map
|
| 108 |
+
def get_edges(self, t):
|
| 109 |
+
edge = torch.cuda.ByteTensor(t.size()).zero_()
|
| 110 |
+
edge[:,:,:,1:] = edge[:,:,:,1:] | (t[:,:,:,1:] != t[:,:,:,:-1])
|
| 111 |
+
edge[:,:,:,:-1] = edge[:,:,:,:-1] | (t[:,:,:,1:] != t[:,:,:,:-1])
|
| 112 |
+
edge[:,:,1:,:] = edge[:,:,1:,:] | (t[:,:,1:,:] != t[:,:,:-1,:])
|
| 113 |
+
edge[:,:,:-1,:] = edge[:,:,:-1,:] | (t[:,:,1:,:] != t[:,:,:-1,:])
|
| 114 |
+
return edge.float()
|
| 115 |
+
|
| 116 |
+
# change the label at the source position to the label at the target position
|
| 117 |
+
def change_labels(self, click_src, click_tgt):
|
| 118 |
+
y_src, x_src = click_src[0], click_src[1]
|
| 119 |
+
y_tgt, x_tgt = click_tgt[0], click_tgt[1]
|
| 120 |
+
label_src = int(self.label_map[0, 0, y_src, x_src])
|
| 121 |
+
inst_src = self.inst_map[0, 0, y_src, x_src]
|
| 122 |
+
label_tgt = int(self.label_map[0, 0, y_tgt, x_tgt])
|
| 123 |
+
inst_tgt = self.inst_map[0, 0, y_tgt, x_tgt]
|
| 124 |
+
|
| 125 |
+
idx_src = (self.inst_map == inst_src).nonzero()
|
| 126 |
+
# need to change 3 things: label map, instance map, and feature map
|
| 127 |
+
if idx_src.shape:
|
| 128 |
+
# backup current maps
|
| 129 |
+
self.backup_current_state()
|
| 130 |
+
|
| 131 |
+
# change both the label map and the network input
|
| 132 |
+
self.label_map[idx_src[:,0], idx_src[:,1], idx_src[:,2], idx_src[:,3]] = label_tgt
|
| 133 |
+
self.net_input[idx_src[:,0], idx_src[:,1] + label_src, idx_src[:,2], idx_src[:,3]] = 0
|
| 134 |
+
self.net_input[idx_src[:,0], idx_src[:,1] + label_tgt, idx_src[:,2], idx_src[:,3]] = 1
|
| 135 |
+
|
| 136 |
+
# update the instance map (and the network input)
|
| 137 |
+
if inst_tgt > 1000:
|
| 138 |
+
# if different instances have different ids, give the new object a new id
|
| 139 |
+
tgt_indices = (self.inst_map > label_tgt * 1000) & (self.inst_map < (label_tgt+1) * 1000)
|
| 140 |
+
inst_tgt = self.inst_map[tgt_indices].max() + 1
|
| 141 |
+
self.inst_map[idx_src[:,0], idx_src[:,1], idx_src[:,2], idx_src[:,3]] = inst_tgt
|
| 142 |
+
self.net_input[:,-1,:,:] = self.get_edges(self.inst_map)
|
| 143 |
+
|
| 144 |
+
# also copy the source features to the target position
|
| 145 |
+
idx_tgt = (self.inst_map == inst_tgt).nonzero()
|
| 146 |
+
if idx_tgt.shape:
|
| 147 |
+
self.copy_features(idx_src, idx_tgt[0,:])
|
| 148 |
+
|
| 149 |
+
self.fake_image = util.tensor2im(self.single_forward(self.net_input, self.feat_map))
|
| 150 |
+
|
| 151 |
+
# add strokes of target label in the image
|
| 152 |
+
def add_strokes(self, click_src, label_tgt, bw, save):
|
| 153 |
+
# get the region of the new strokes (bw is the brush width)
|
| 154 |
+
size = self.net_input.size()
|
| 155 |
+
h, w = size[2], size[3]
|
| 156 |
+
idx_src = torch.LongTensor(bw**2, 4).fill_(0)
|
| 157 |
+
for i in range(bw):
|
| 158 |
+
idx_src[i*bw:(i+1)*bw, 2] = min(h-1, max(0, click_src[0]-bw//2 + i))
|
| 159 |
+
for j in range(bw):
|
| 160 |
+
idx_src[i*bw+j, 3] = min(w-1, max(0, click_src[1]-bw//2 + j))
|
| 161 |
+
idx_src = idx_src.cuda()
|
| 162 |
+
|
| 163 |
+
# again, need to update 3 things
|
| 164 |
+
if idx_src.shape:
|
| 165 |
+
# backup current maps
|
| 166 |
+
if save:
|
| 167 |
+
self.backup_current_state()
|
| 168 |
+
|
| 169 |
+
# update the label map (and the network input) in the stroke region
|
| 170 |
+
self.label_map[idx_src[:,0], idx_src[:,1], idx_src[:,2], idx_src[:,3]] = label_tgt
|
| 171 |
+
for k in range(self.opt.label_nc):
|
| 172 |
+
self.net_input[idx_src[:,0], idx_src[:,1] + k, idx_src[:,2], idx_src[:,3]] = 0
|
| 173 |
+
self.net_input[idx_src[:,0], idx_src[:,1] + label_tgt, idx_src[:,2], idx_src[:,3]] = 1
|
| 174 |
+
|
| 175 |
+
# update the instance map (and the network input)
|
| 176 |
+
self.inst_map[idx_src[:,0], idx_src[:,1], idx_src[:,2], idx_src[:,3]] = label_tgt
|
| 177 |
+
self.net_input[:,-1,:,:] = self.get_edges(self.inst_map)
|
| 178 |
+
|
| 179 |
+
# also update the features if available
|
| 180 |
+
if self.opt.instance_feat:
|
| 181 |
+
feat = self.features_clustered[label_tgt]
|
| 182 |
+
#np.random.seed(label_tgt+1)
|
| 183 |
+
#cluster_idx = np.random.randint(0, feat.shape[0])
|
| 184 |
+
cluster_idx = self.cluster_indices[label_tgt]
|
| 185 |
+
self.set_features(idx_src, feat, cluster_idx)
|
| 186 |
+
|
| 187 |
+
self.fake_image = util.tensor2im(self.single_forward(self.net_input, self.feat_map))
|
| 188 |
+
|
| 189 |
+
# add an object to the clicked position with selected style
|
| 190 |
+
def add_objects(self, click_src, label_tgt, mask, style_id=0):
|
| 191 |
+
y, x = click_src[0], click_src[1]
|
| 192 |
+
mask = np.transpose(mask, (2, 0, 1))[np.newaxis,...]
|
| 193 |
+
idx_src = torch.from_numpy(mask).cuda().nonzero()
|
| 194 |
+
idx_src[:,2] += y
|
| 195 |
+
idx_src[:,3] += x
|
| 196 |
+
|
| 197 |
+
# backup current maps
|
| 198 |
+
self.backup_current_state()
|
| 199 |
+
|
| 200 |
+
# update label map
|
| 201 |
+
self.label_map[idx_src[:,0], idx_src[:,1], idx_src[:,2], idx_src[:,3]] = label_tgt
|
| 202 |
+
for k in range(self.opt.label_nc):
|
| 203 |
+
self.net_input[idx_src[:,0], idx_src[:,1] + k, idx_src[:,2], idx_src[:,3]] = 0
|
| 204 |
+
self.net_input[idx_src[:,0], idx_src[:,1] + label_tgt, idx_src[:,2], idx_src[:,3]] = 1
|
| 205 |
+
|
| 206 |
+
# update instance map
|
| 207 |
+
self.inst_map[idx_src[:,0], idx_src[:,1], idx_src[:,2], idx_src[:,3]] = label_tgt
|
| 208 |
+
self.net_input[:,-1,:,:] = self.get_edges(self.inst_map)
|
| 209 |
+
|
| 210 |
+
# update feature map
|
| 211 |
+
self.set_features(idx_src, self.feat, style_id)
|
| 212 |
+
|
| 213 |
+
self.fake_image = util.tensor2im(self.single_forward(self.net_input, self.feat_map))
|
| 214 |
+
|
| 215 |
+
def single_forward(self, net_input, feat_map):
|
| 216 |
+
net_input = torch.cat((net_input, feat_map), dim=1)
|
| 217 |
+
fake_image = self.netG.forward(net_input)
|
| 218 |
+
|
| 219 |
+
if fake_image.size()[0] == 1:
|
| 220 |
+
return fake_image.data[0]
|
| 221 |
+
return fake_image.data
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
# generate all outputs for different styles
|
| 225 |
+
def style_forward(self, click_pt, style_id=-1):
|
| 226 |
+
if click_pt is None:
|
| 227 |
+
self.fake_image = util.tensor2im(self.single_forward(self.net_input, self.feat_map))
|
| 228 |
+
self.crop = None
|
| 229 |
+
self.mask = None
|
| 230 |
+
else:
|
| 231 |
+
instToChange = int(self.object_map[0, 0, click_pt[0], click_pt[1]])
|
| 232 |
+
self.instToChange = instToChange
|
| 233 |
+
label = instToChange if instToChange < 1000 else instToChange//1000
|
| 234 |
+
self.feat = self.features_clustered[label]
|
| 235 |
+
self.fake_image = []
|
| 236 |
+
self.mask = self.object_map == instToChange
|
| 237 |
+
idx = self.mask.nonzero()
|
| 238 |
+
self.get_crop_region(idx)
|
| 239 |
+
if idx.size():
|
| 240 |
+
if style_id == -1:
|
| 241 |
+
(min_y, min_x, max_y, max_x) = self.crop
|
| 242 |
+
### original
|
| 243 |
+
for cluster_idx in range(self.opt.multiple_output):
|
| 244 |
+
self.set_features(idx, self.feat, cluster_idx)
|
| 245 |
+
fake_image = self.single_forward(self.net_input, self.feat_map)
|
| 246 |
+
fake_image = util.tensor2im(fake_image[:,min_y:max_y,min_x:max_x])
|
| 247 |
+
self.fake_image.append(fake_image)
|
| 248 |
+
"""### To speed up previewing different style results, either crop or downsample the label maps
|
| 249 |
+
if instToChange > 1000:
|
| 250 |
+
(min_y, min_x, max_y, max_x) = self.crop
|
| 251 |
+
### crop
|
| 252 |
+
_, _, h, w = self.net_input.size()
|
| 253 |
+
offset = 512
|
| 254 |
+
y_start, x_start = max(0, min_y-offset), max(0, min_x-offset)
|
| 255 |
+
y_end, x_end = min(h, (max_y + offset)), min(w, (max_x + offset))
|
| 256 |
+
y_region = slice(y_start, y_start+(y_end-y_start)//16*16)
|
| 257 |
+
x_region = slice(x_start, x_start+(x_end-x_start)//16*16)
|
| 258 |
+
net_input = self.net_input[:,:,y_region,x_region]
|
| 259 |
+
for cluster_idx in range(self.opt.multiple_output):
|
| 260 |
+
self.set_features(idx, self.feat, cluster_idx)
|
| 261 |
+
fake_image = self.single_forward(net_input, self.feat_map[:,:,y_region,x_region])
|
| 262 |
+
fake_image = util.tensor2im(fake_image[:,min_y-y_start:max_y-y_start,min_x-x_start:max_x-x_start])
|
| 263 |
+
self.fake_image.append(fake_image)
|
| 264 |
+
else:
|
| 265 |
+
### downsample
|
| 266 |
+
(min_y, min_x, max_y, max_x) = [crop//2 for crop in self.crop]
|
| 267 |
+
net_input = self.net_input[:,:,::2,::2]
|
| 268 |
+
size = net_input.size()
|
| 269 |
+
net_input_batch = net_input.expand(self.opt.multiple_output, size[1], size[2], size[3])
|
| 270 |
+
for cluster_idx in range(self.opt.multiple_output):
|
| 271 |
+
self.set_features(idx, self.feat, cluster_idx)
|
| 272 |
+
feat_map = self.feat_map[:,:,::2,::2]
|
| 273 |
+
if cluster_idx == 0:
|
| 274 |
+
feat_map_batch = feat_map
|
| 275 |
+
else:
|
| 276 |
+
feat_map_batch = torch.cat((feat_map_batch, feat_map), dim=0)
|
| 277 |
+
fake_image_batch = self.single_forward(net_input_batch, feat_map_batch)
|
| 278 |
+
for i in range(self.opt.multiple_output):
|
| 279 |
+
self.fake_image.append(util.tensor2im(fake_image_batch[i,:,min_y:max_y,min_x:max_x]))"""
|
| 280 |
+
|
| 281 |
+
else:
|
| 282 |
+
self.set_features(idx, self.feat, style_id)
|
| 283 |
+
self.cluster_indices[label] = style_id
|
| 284 |
+
self.fake_image = util.tensor2im(self.single_forward(self.net_input, self.feat_map))
|
| 285 |
+
|
| 286 |
+
def backup_current_state(self):
|
| 287 |
+
self.net_input_prev = self.net_input.clone()
|
| 288 |
+
self.label_map_prev = self.label_map.clone()
|
| 289 |
+
self.inst_map_prev = self.inst_map.clone()
|
| 290 |
+
self.feat_map_prev = self.feat_map.clone()
|
| 291 |
+
|
| 292 |
+
# crop the ROI and get the mask of the object
|
| 293 |
+
def get_crop_region(self, idx):
|
| 294 |
+
size = self.net_input.size()
|
| 295 |
+
h, w = size[2], size[3]
|
| 296 |
+
min_y, min_x = idx[:,2].min(), idx[:,3].min()
|
| 297 |
+
max_y, max_x = idx[:,2].max(), idx[:,3].max()
|
| 298 |
+
crop_min = 128
|
| 299 |
+
if max_y - min_y < crop_min:
|
| 300 |
+
min_y = max(0, (max_y + min_y) // 2 - crop_min // 2)
|
| 301 |
+
max_y = min(h-1, min_y + crop_min)
|
| 302 |
+
if max_x - min_x < crop_min:
|
| 303 |
+
min_x = max(0, (max_x + min_x) // 2 - crop_min // 2)
|
| 304 |
+
max_x = min(w-1, min_x + crop_min)
|
| 305 |
+
self.crop = (min_y, min_x, max_y, max_x)
|
| 306 |
+
self.mask = self.mask[:,:, min_y:max_y, min_x:max_x]
|
| 307 |
+
|
| 308 |
+
# update the feature map once a new object is added or the label is changed
|
| 309 |
+
def update_features(self, cluster_idx, mask=None, click_pt=None):
|
| 310 |
+
self.feat_map_prev = self.feat_map.clone()
|
| 311 |
+
# adding a new object
|
| 312 |
+
if mask is not None:
|
| 313 |
+
y, x = click_pt[0], click_pt[1]
|
| 314 |
+
mask = np.transpose(mask, (2,0,1))[np.newaxis,...]
|
| 315 |
+
idx = torch.from_numpy(mask).cuda().nonzero()
|
| 316 |
+
idx[:,2] += y
|
| 317 |
+
idx[:,3] += x
|
| 318 |
+
# changing the label of an existing object
|
| 319 |
+
else:
|
| 320 |
+
idx = (self.object_map == self.instToChange).nonzero()
|
| 321 |
+
|
| 322 |
+
# update feature map
|
| 323 |
+
self.set_features(idx, self.feat, cluster_idx)
|
| 324 |
+
|
| 325 |
+
# set the class features to the target feature
|
| 326 |
+
def set_features(self, idx, feat, cluster_idx):
|
| 327 |
+
for k in range(self.opt.feat_num):
|
| 328 |
+
self.feat_map[idx[:,0], idx[:,1] + k, idx[:,2], idx[:,3]] = feat[cluster_idx, k]
|
| 329 |
+
|
| 330 |
+
# copy the features at the target position to the source position
|
| 331 |
+
def copy_features(self, idx_src, idx_tgt):
|
| 332 |
+
for k in range(self.opt.feat_num):
|
| 333 |
+
val = self.feat_map[idx_tgt[0], idx_tgt[1] + k, idx_tgt[2], idx_tgt[3]]
|
| 334 |
+
self.feat_map[idx_src[:,0], idx_src[:,1] + k, idx_src[:,2], idx_src[:,3]] = val
|
| 335 |
+
|
| 336 |
+
def get_current_visuals(self, getLabel=False):
|
| 337 |
+
mask = self.mask
|
| 338 |
+
if self.mask is not None:
|
| 339 |
+
mask = np.transpose(self.mask[0].cpu().float().numpy(), (1,2,0)).astype(np.uint8)
|
| 340 |
+
|
| 341 |
+
dict_list = [('fake_image', self.fake_image), ('mask', mask)]
|
| 342 |
+
|
| 343 |
+
if getLabel: # only output label map if needed to save bandwidth
|
| 344 |
+
label = util.tensor2label(self.net_input.data[0], self.opt.label_nc)
|
| 345 |
+
dict_list += [('label', label)]
|
| 346 |
+
|
| 347 |
+
return OrderedDict(dict_list)
|
options/__init__.py
ADDED
|
File without changes
|
options/base_options.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import os
|
| 3 |
+
from util import util
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
class BaseOptions():
|
| 7 |
+
def __init__(self):
|
| 8 |
+
self.parser = argparse.ArgumentParser()
|
| 9 |
+
self.initialized = False
|
| 10 |
+
|
| 11 |
+
def initialize(self):
|
| 12 |
+
# experiment specifics
|
| 13 |
+
self.parser.add_argument('--name', type=str, default='label2city', help='name of the experiment. It decides where to store samples and models')
|
| 14 |
+
self.parser.add_argument('--gpu_ids', type=str, default='0', help='gpu ids: e.g. 0 0,1,2, 0,2. use -1 for CPU')
|
| 15 |
+
self.parser.add_argument('--checkpoints_dir', type=str, default='./trained_models', help='models are saved here')
|
| 16 |
+
self.parser.add_argument('--model', type=str, default='pix2pixHD', help='which model to use')
|
| 17 |
+
self.parser.add_argument('--norm', type=str, default='instance', help='instance normalization or batch normalization')
|
| 18 |
+
self.parser.add_argument('--use_dropout', action='store_true', help='use dropout for the generator')
|
| 19 |
+
self.parser.add_argument('--data_type', default=32, type=int, choices=[8, 16, 32], help="Supported data type i.e. 8, 16, 32 bit")
|
| 20 |
+
self.parser.add_argument('--verbose', action='store_true', default=False, help='toggles verbose')
|
| 21 |
+
self.parser.add_argument('--fp16', action='store_true', default=False, help='train with AMP')
|
| 22 |
+
self.parser.add_argument('--local_rank', type=int, default=0, help='local rank for distributed training')
|
| 23 |
+
|
| 24 |
+
# input/output sizes
|
| 25 |
+
self.parser.add_argument('--batchSize', type=int, default=1, help='input batch size')
|
| 26 |
+
self.parser.add_argument('--loadSize', type=int, default=256, help='scale images to this size')
|
| 27 |
+
self.parser.add_argument('--fineSize', type=int, default=256, help='then crop to this size')
|
| 28 |
+
self.parser.add_argument('--label_nc', type=int, default=35, help='# of input label channels')
|
| 29 |
+
self.parser.add_argument('--input_nc', type=int, default=3, help='# of input image channels')
|
| 30 |
+
self.parser.add_argument('--output_nc', type=int, default=3, help='# of output image channels')
|
| 31 |
+
|
| 32 |
+
# for setting inputs
|
| 33 |
+
self.parser.add_argument('--dataroot', type=str, default=r'F:\Datasets\DigestPath\scene_generation\all\1000\256\split\train')
|
| 34 |
+
self.parser.add_argument('--resize_or_crop', type=str, default='scale_width', help='scaling and cropping of images at load time [resize_and_crop|crop|scale_width|scale_width_and_crop]')
|
| 35 |
+
self.parser.add_argument('--serial_batches', action='store_true', help='if true, takes images in order to make batches, otherwise takes them randomly')
|
| 36 |
+
self.parser.add_argument('--no_flip', action='store_true', help='if specified, do not flip the images for data argumentation')
|
| 37 |
+
self.parser.add_argument('--nThreads', default=0, type=int, help='# threads for loading data')
|
| 38 |
+
self.parser.add_argument('--max_dataset_size', type=int, default=float("inf"), help='Maximum number of samples allowed per dataset. If the dataset directory contains more than max_dataset_size, only a subset is loaded.')
|
| 39 |
+
|
| 40 |
+
# for displays
|
| 41 |
+
self.parser.add_argument('--display_winsize', type=int, default=512, help='display window size')
|
| 42 |
+
self.parser.add_argument('--tf_log', action='store_true', help='if specified, use tensorboard logging. Requires tensorflow installed')
|
| 43 |
+
|
| 44 |
+
# for generator
|
| 45 |
+
self.parser.add_argument('--netG', type=str, default='global', help='selects model to use for netG')
|
| 46 |
+
self.parser.add_argument('--ngf', type=int, default=64, help='# of gen filters in first conv layer')
|
| 47 |
+
self.parser.add_argument('--n_downsample_global', type=int, default=4, help='number of downsampling layers in netG')
|
| 48 |
+
self.parser.add_argument('--n_blocks_global', type=int, default=9, help='number of residual blocks in the global generator network')
|
| 49 |
+
self.parser.add_argument('--n_blocks_local', type=int, default=3, help='number of residual blocks in the local enhancer network')
|
| 50 |
+
self.parser.add_argument('--n_local_enhancers', type=int, default=1, help='number of local enhancers to use')
|
| 51 |
+
self.parser.add_argument('--niter_fix_global', type=int, default=0, help='number of epochs that we only train the outmost local enhancer')
|
| 52 |
+
|
| 53 |
+
# for instance-wise features
|
| 54 |
+
self.parser.add_argument('--no_instance', action='store_true', help='if specified, do *not* add instance map as input')
|
| 55 |
+
self.parser.add_argument('--instance_feat', action='store_true', help='if specified, add encoded instance features as input')
|
| 56 |
+
self.parser.add_argument('--label_feat', action='store_true', help='if specified, add encoded label features as input')
|
| 57 |
+
self.parser.add_argument('--feat_num', type=int, default=3, help='vector length for encoded features')
|
| 58 |
+
self.parser.add_argument('--load_features', action='store_true', help='if specified, load precomputed feature maps')
|
| 59 |
+
self.parser.add_argument('--n_downsample_E', type=int, default=4, help='# of downsampling layers in encoder')
|
| 60 |
+
self.parser.add_argument('--nef', type=int, default=16, help='# of encoder filters in the first conv layer')
|
| 61 |
+
self.parser.add_argument('--n_clusters', type=int, default=10, help='number of clusters for features')
|
| 62 |
+
|
| 63 |
+
self.initialized = True
|
| 64 |
+
|
| 65 |
+
def parse(self, save=True):
|
| 66 |
+
if not self.initialized:
|
| 67 |
+
self.initialize()
|
| 68 |
+
self.opt = self.parser.parse_args()
|
| 69 |
+
self.opt.isTrain = self.isTrain # train or test
|
| 70 |
+
|
| 71 |
+
str_ids = self.opt.gpu_ids.split(',')
|
| 72 |
+
self.opt.gpu_ids = []
|
| 73 |
+
for str_id in str_ids:
|
| 74 |
+
id = int(str_id)
|
| 75 |
+
if id >= 0:
|
| 76 |
+
self.opt.gpu_ids.append(id)
|
| 77 |
+
|
| 78 |
+
# set gpu ids
|
| 79 |
+
if len(self.opt.gpu_ids) > 0:
|
| 80 |
+
torch.cuda.set_device(self.opt.gpu_ids[0])
|
| 81 |
+
|
| 82 |
+
args = vars(self.opt)
|
| 83 |
+
|
| 84 |
+
print('------------ Options -------------')
|
| 85 |
+
for k, v in sorted(args.items()):
|
| 86 |
+
print('%s: %s' % (str(k), str(v)))
|
| 87 |
+
print('-------------- End ----------------')
|
| 88 |
+
|
| 89 |
+
# save to the disk
|
| 90 |
+
expr_dir = os.path.join(self.opt.checkpoints_dir, self.opt.name)
|
| 91 |
+
util.mkdirs(expr_dir)
|
| 92 |
+
if save and not self.opt.continue_train:
|
| 93 |
+
file_name = os.path.join(expr_dir, 'opt.txt')
|
| 94 |
+
with open(file_name, 'wt') as opt_file:
|
| 95 |
+
opt_file.write('------------ Options -------------\n')
|
| 96 |
+
for k, v in sorted(args.items()):
|
| 97 |
+
opt_file.write('%s: %s\n' % (str(k), str(v)))
|
| 98 |
+
opt_file.write('-------------- End ----------------\n')
|
| 99 |
+
return self.opt
|
options/test_options.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .base_options import BaseOptions
|
| 2 |
+
|
| 3 |
+
class TestOptions(BaseOptions):
|
| 4 |
+
def initialize(self):
|
| 5 |
+
BaseOptions.initialize(self)
|
| 6 |
+
self.parser.add_argument('--ntest', type=int, default=float("inf"), help='# of test examples.')
|
| 7 |
+
self.parser.add_argument('--results_dir', type=str, default='./tmp', help='saves results here.')
|
| 8 |
+
self.parser.add_argument('--aspect_ratio', type=float, default=1.0, help='aspect ratio of result images')
|
| 9 |
+
self.parser.add_argument('--phase', type=str, default='test', help='train, val, test, etc')
|
| 10 |
+
self.parser.add_argument('--which_epoch', type=str, default='latest', help='which epoch to load? set to latest to use latest cached model')
|
| 11 |
+
self.parser.add_argument('--how_many', type=int, default=3000, help='how many test images to run')
|
| 12 |
+
self.parser.add_argument('--cluster_path', type=str, default='features_clustered_010.npy', help='the path for clustered results of encoded features')
|
| 13 |
+
self.parser.add_argument('--use_encoded_image', action='store_true', help='if specified, encode the real image to get the feature map')
|
| 14 |
+
self.parser.add_argument("--export_onnx", type=str, help="export ONNX model to a given file")
|
| 15 |
+
self.parser.add_argument("--engine", type=str, help="run serialized TRT engine")
|
| 16 |
+
self.parser.add_argument("--onnx", type=str, help="run ONNX model via TRT")
|
| 17 |
+
self.isTrain = False
|
options/train_options.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .base_options import BaseOptions
|
| 2 |
+
|
| 3 |
+
class TrainOptions(BaseOptions):
|
| 4 |
+
def initialize(self):
|
| 5 |
+
BaseOptions.initialize(self)
|
| 6 |
+
# for displays
|
| 7 |
+
self.parser.add_argument('--display_freq', type=int, default=100, help='frequency of showing training results on screen')
|
| 8 |
+
self.parser.add_argument('--print_freq', type=int, default=100, help='frequency of showing training results on console')
|
| 9 |
+
self.parser.add_argument('--save_latest_freq', type=int, default=1000, help='frequency of saving the latest results')
|
| 10 |
+
self.parser.add_argument('--save_epoch_freq', type=int, default=10, help='frequency of saving checkpoints at the end of epochs')
|
| 11 |
+
self.parser.add_argument('--no_html', action='store_true', help='do not save intermediate training results to [opt.checkpoints_dir]/[opt.name]/web/')
|
| 12 |
+
self.parser.add_argument('--debug', action='store_true', help='only do one epoch and displays at each iteration')
|
| 13 |
+
|
| 14 |
+
# for training
|
| 15 |
+
self.parser.add_argument('--continue_train', action='store_true', help='continue training: load the latest model')
|
| 16 |
+
self.parser.add_argument('--load_pretrain', type=str, default='', help='load the pretrained model from the specified location')
|
| 17 |
+
self.parser.add_argument('--which_epoch', type=str, default='latest', help='which epoch to load? set to latest to use latest cached model')
|
| 18 |
+
self.parser.add_argument('--phase', type=str, default='train', help='train, val, test, etc')
|
| 19 |
+
self.parser.add_argument('--niter', type=int, default=100, help='# of iter at starting learning rate')
|
| 20 |
+
self.parser.add_argument('--niter_decay', type=int, default=100, help='# of iter to linearly decay learning rate to zero')
|
| 21 |
+
self.parser.add_argument('--beta1', type=float, default=0.5, help='momentum term of adam')
|
| 22 |
+
self.parser.add_argument('--lr', type=float, default=0.0002, help='initial learning rate for adam')
|
| 23 |
+
|
| 24 |
+
# for discriminators
|
| 25 |
+
self.parser.add_argument('--num_D', type=int, default=2, help='number of discriminators to use')
|
| 26 |
+
self.parser.add_argument('--n_layers_D', type=int, default=3, help='only used if which_model_netD==n_layers')
|
| 27 |
+
self.parser.add_argument('--ndf', type=int, default=64, help='# of discrim filters in first conv layer')
|
| 28 |
+
self.parser.add_argument('--lambda_feat', type=float, default=10.0, help='weight for feature matching loss')
|
| 29 |
+
self.parser.add_argument('--no_ganFeat_loss', action='store_true', help='if specified, do *not* use discriminator feature matching loss')
|
| 30 |
+
self.parser.add_argument('--no_vgg_loss', action='store_true', help='if specified, do *not* use VGG feature matching loss')
|
| 31 |
+
self.parser.add_argument('--no_lsgan', action='store_true', help='do *not* use least square GAN, if false, use vanilla GAN')
|
| 32 |
+
self.parser.add_argument('--pool_size', type=int, default=0, help='the size of image buffer that stores previously generated images')
|
| 33 |
+
|
| 34 |
+
self.isTrain = True
|
pix2pixhd_test.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from collections import OrderedDict
|
| 3 |
+
from torch.autograd import Variable
|
| 4 |
+
from options.test_options import TestOptions
|
| 5 |
+
from data.data_loader import CreateDataLoader
|
| 6 |
+
from models.models import create_model
|
| 7 |
+
import util.util as util
|
| 8 |
+
from util.visualizer import Visualizer
|
| 9 |
+
from util import html
|
| 10 |
+
import torch
|
| 11 |
+
|
| 12 |
+
opt = TestOptions().parse(save=False)
|
| 13 |
+
opt.nThreads = 0 # test code only supports nThreads = 1 but for Srijay's windows machine it will be 0
|
| 14 |
+
opt.batchSize = 1 # test code only supports batchSize = 1
|
| 15 |
+
opt.serial_batches = True # no shuffle
|
| 16 |
+
opt.no_flip = True # no flip
|
| 17 |
+
|
| 18 |
+
data_loader = CreateDataLoader(opt)
|
| 19 |
+
dataset = data_loader.load_data()
|
| 20 |
+
visualizer = Visualizer(opt)
|
| 21 |
+
# create website
|
| 22 |
+
web_dir = os.path.join(opt.results_dir, opt.name, '%s_%s' % (opt.phase, opt.which_epoch))
|
| 23 |
+
webpage = html.HTML(web_dir, 'Experiment = %s, Phase = %s, Epoch = %s' % (opt.name, opt.phase, opt.which_epoch))
|
| 24 |
+
|
| 25 |
+
# test
|
| 26 |
+
if not opt.engine and not opt.onnx:
|
| 27 |
+
model = create_model(opt)
|
| 28 |
+
if opt.data_type == 16:
|
| 29 |
+
model.half()
|
| 30 |
+
elif opt.data_type == 8:
|
| 31 |
+
model.type(torch.uint8)
|
| 32 |
+
|
| 33 |
+
if opt.verbose:
|
| 34 |
+
print(model)
|
| 35 |
+
else:
|
| 36 |
+
from run_engine import run_trt_engine, run_onnx
|
| 37 |
+
|
| 38 |
+
for i, data in enumerate(dataset):
|
| 39 |
+
if i >= opt.how_many:
|
| 40 |
+
break
|
| 41 |
+
if opt.data_type == 16:
|
| 42 |
+
data['label'] = data['label'].half()
|
| 43 |
+
data['inst'] = data['inst'].half()
|
| 44 |
+
elif opt.data_type == 8:
|
| 45 |
+
data['label'] = data['label'].uint8()
|
| 46 |
+
data['inst'] = data['inst'].uint8()
|
| 47 |
+
if opt.export_onnx:
|
| 48 |
+
print ("Exporting to ONNX: ", opt.export_onnx)
|
| 49 |
+
assert opt.export_onnx.endswith("onnx"), "Export model file should end with .onnx"
|
| 50 |
+
torch.onnx.export(model, [data['label'], data['inst']],
|
| 51 |
+
opt.export_onnx, verbose=True)
|
| 52 |
+
exit(0)
|
| 53 |
+
minibatch = 1
|
| 54 |
+
if opt.engine:
|
| 55 |
+
generated = run_trt_engine(opt.engine, minibatch, [data['label'], data['inst']])
|
| 56 |
+
elif opt.onnx:
|
| 57 |
+
generated = run_onnx(opt.onnx, opt.data_type, minibatch, [data['label'], data['inst']])
|
| 58 |
+
else:
|
| 59 |
+
generated = model.inference(data['label'], data['inst'], data['image'])
|
| 60 |
+
|
| 61 |
+
visuals = OrderedDict([('input_label', util.tensor2label(data['label'][0], opt.label_nc)),
|
| 62 |
+
('synthesized_image', util.tensor2im(generated.data[0]))])
|
| 63 |
+
img_path = data['path']
|
| 64 |
+
print('process image... %s' % img_path)
|
| 65 |
+
visualizer.save_images(webpage, visuals, img_path)
|
| 66 |
+
|
| 67 |
+
webpage.save()
|
pixelcnn/gated_pixelcnn.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
from torchvision import datasets, transforms
|
| 4 |
+
import numpy as np
|
| 5 |
+
from torchvision.utils import save_image
|
| 6 |
+
import time
|
| 7 |
+
import os
|
| 8 |
+
import sys
|
| 9 |
+
|
| 10 |
+
"""
|
| 11 |
+
add vqvae and pixelcnn dirs to path
|
| 12 |
+
make sure you run from vqvae directory
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
current_dir = sys.path.append(os.getcwd())
|
| 16 |
+
pixelcnn_dir = sys.path.append(os.getcwd()+ '/pixelcnn')
|
| 17 |
+
|
| 18 |
+
from pixelcnn.models import GatedPixelCNN
|
| 19 |
+
import utils
|
| 20 |
+
|
| 21 |
+
"""
|
| 22 |
+
Hyperparameters
|
| 23 |
+
"""
|
| 24 |
+
import argparse
|
| 25 |
+
parser = argparse.ArgumentParser()
|
| 26 |
+
|
| 27 |
+
parser.add_argument("--batch_size", type=int, default=1)
|
| 28 |
+
parser.add_argument("--epochs", type=int, default=100)
|
| 29 |
+
parser.add_argument("--log_interval", type=int, default=100)
|
| 30 |
+
parser.add_argument("-save", action="store_true")
|
| 31 |
+
parser.add_argument("-gen_samples", action="store_true")
|
| 32 |
+
|
| 33 |
+
parser.add_argument("--dataset", type=str, default='LATENT_BLOCK')
|
| 34 |
+
parser.add_argument("--num_workers", type=int, default=0)
|
| 35 |
+
parser.add_argument("--img_dim", type=int, default=64)
|
| 36 |
+
parser.add_argument("--input_dim", type=int, default=1,
|
| 37 |
+
help='1 for grayscale 3 for rgb')
|
| 38 |
+
parser.add_argument("--n_embeddings", type=int, default=3,
|
| 39 |
+
help='number of embeddings from VQ VAE')
|
| 40 |
+
parser.add_argument("--n_layers", type=int, default=5)
|
| 41 |
+
parser.add_argument("--learning_rate", type=float, default=3e-4)
|
| 42 |
+
|
| 43 |
+
args = parser.parse_args()
|
| 44 |
+
|
| 45 |
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 46 |
+
|
| 47 |
+
"""
|
| 48 |
+
data loaders
|
| 49 |
+
"""
|
| 50 |
+
_, _, train_loader, test_loader, _ = utils.load_data_and_data_loaders('LATENT_BLOCK', args.batch_size)
|
| 51 |
+
|
| 52 |
+
model = GatedPixelCNN(args.n_embeddings, args.img_dim**2, args.n_layers).to(device)
|
| 53 |
+
criterion = nn.CrossEntropyLoss().cuda()
|
| 54 |
+
opt = torch.optim.Adam(model.parameters(), lr=args.learning_rate)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
"""
|
| 58 |
+
train, test, and log
|
| 59 |
+
"""
|
| 60 |
+
|
| 61 |
+
def train():
|
| 62 |
+
train_loss = []
|
| 63 |
+
for batch_idx, (x, label) in enumerate(train_loader):
|
| 64 |
+
start_time = time.time()
|
| 65 |
+
# if args.dataset == 'LATENT_BLOCK':
|
| 66 |
+
# x = (x[:, 0]).cuda()
|
| 67 |
+
# else:
|
| 68 |
+
# x = (x[:, 0] * (K-1)).long().cuda()
|
| 69 |
+
x = x.cuda()
|
| 70 |
+
label = label.cuda()
|
| 71 |
+
|
| 72 |
+
# Train PixelCNN with images
|
| 73 |
+
logits = model(x, label)
|
| 74 |
+
print(logits.shape)
|
| 75 |
+
exit(0)
|
| 76 |
+
logits = logits.permute(0, 2, 3, 1).contiguous()
|
| 77 |
+
|
| 78 |
+
loss = criterion(
|
| 79 |
+
logits.view(-1, args.n_embeddings),
|
| 80 |
+
x.view(-1)
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
opt.zero_grad()
|
| 84 |
+
loss.backward()
|
| 85 |
+
opt.step()
|
| 86 |
+
|
| 87 |
+
train_loss.append(loss.item())
|
| 88 |
+
|
| 89 |
+
if (batch_idx + 1) % args.log_interval == 0:
|
| 90 |
+
print('\tIter: [{}/{} ({:.0f}%)]\tLoss: {} Time: {}'.format(
|
| 91 |
+
batch_idx * len(x), len(train_loader.dataset),
|
| 92 |
+
args.log_interval * batch_idx / len(train_loader),
|
| 93 |
+
np.asarray(train_loss)[-args.log_interval:].mean(0),
|
| 94 |
+
time.time() - start_time
|
| 95 |
+
))
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def test():
|
| 99 |
+
start_time = time.time()
|
| 100 |
+
val_loss = []
|
| 101 |
+
with torch.no_grad():
|
| 102 |
+
for batch_idx, (x, label) in enumerate(test_loader):
|
| 103 |
+
if args.dataset == 'LATENT_BLOCK':
|
| 104 |
+
x = (x[:, 0]).cuda()
|
| 105 |
+
else:
|
| 106 |
+
x = (x[:, 0] * (args.n_embeddings-1)).long().cuda()
|
| 107 |
+
label = label.cuda()
|
| 108 |
+
|
| 109 |
+
logits = model(x, label)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
logits = logits.permute(0, 2, 3, 1).contiguous()
|
| 113 |
+
loss = criterion(
|
| 114 |
+
logits.view(-1, args.n_embeddings),
|
| 115 |
+
x.view(-1)
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
+
val_loss.append(loss.item())
|
| 119 |
+
|
| 120 |
+
print('Validation Completed!\tLoss: {} Time: {}'.format(
|
| 121 |
+
np.asarray(val_loss).mean(0),
|
| 122 |
+
time.time() - start_time
|
| 123 |
+
))
|
| 124 |
+
return np.asarray(val_loss).mean(0)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def generate_samples(epoch):
|
| 128 |
+
label = torch.arange(10).expand(10, 10).contiguous().view(-1)
|
| 129 |
+
label = label.long().cuda()
|
| 130 |
+
|
| 131 |
+
x_tilde = model.generate(label, shape=(args.img_dim,args.img_dim), batch_size=100)
|
| 132 |
+
|
| 133 |
+
print(x_tilde[0])
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
BEST_LOSS = 999
|
| 138 |
+
LAST_SAVED = -1
|
| 139 |
+
for epoch in range(1, args.epochs):
|
| 140 |
+
print("\nEpoch {}:".format(epoch))
|
| 141 |
+
train()
|
| 142 |
+
cur_loss = test()
|
| 143 |
+
|
| 144 |
+
if args.save or cur_loss <= BEST_LOSS:
|
| 145 |
+
BEST_LOSS = cur_loss
|
| 146 |
+
LAST_SAVED = epoch
|
| 147 |
+
|
| 148 |
+
print("Saving model!")
|
| 149 |
+
torch.save(model.state_dict(), 'results/{}_pixelcnn.pt'.format(args.dataset))
|
| 150 |
+
else:
|
| 151 |
+
print("Not saving model! Last saved: {}".format(LAST_SAVED))
|
| 152 |
+
if args.gen_samples:
|
| 153 |
+
generate_samples(epoch)
|
pixelcnn/models.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
from torch.distributions.normal import Normal
|
| 6 |
+
from torch.distributions import kl_divergence
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def weights_init(m):
|
| 11 |
+
classname = m.__class__.__name__
|
| 12 |
+
if classname.find('Conv') != -1:
|
| 13 |
+
try:
|
| 14 |
+
nn.init.xavier_uniform_(m.weight.data)
|
| 15 |
+
m.bias.data.fill_(0)
|
| 16 |
+
except AttributeError:
|
| 17 |
+
print("Skipping initialization of ", classname)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class GatedActivation(nn.Module):
|
| 22 |
+
def __init__(self):
|
| 23 |
+
super().__init__()
|
| 24 |
+
|
| 25 |
+
def forward(self, x):
|
| 26 |
+
x, y = x.chunk(2, dim=1)
|
| 27 |
+
return F.tanh(x) * F.sigmoid(y)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class GatedMaskedConv2d(nn.Module):
|
| 31 |
+
def __init__(self, mask_type, dim, kernel, residual=True, n_classes=10):
|
| 32 |
+
super().__init__()
|
| 33 |
+
assert kernel % 2 == 1, print("Kernel size must be odd")
|
| 34 |
+
self.mask_type = mask_type
|
| 35 |
+
self.residual = residual
|
| 36 |
+
|
| 37 |
+
self.class_cond_embedding = nn.Embedding(
|
| 38 |
+
n_classes, 2 * dim
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
kernel_shp = (kernel // 2 + 1, kernel) # (ceil(n/2), n)
|
| 42 |
+
padding_shp = (kernel // 2, kernel // 2)
|
| 43 |
+
self.vert_stack = nn.Conv2d(
|
| 44 |
+
dim, dim * 2,
|
| 45 |
+
kernel_shp, 1, padding_shp
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
self.vert_to_horiz = nn.Conv2d(2 * dim, 2 * dim, 1)
|
| 49 |
+
|
| 50 |
+
kernel_shp = (1, kernel // 2 + 1)
|
| 51 |
+
padding_shp = (0, kernel // 2)
|
| 52 |
+
self.horiz_stack = nn.Conv2d(
|
| 53 |
+
dim, dim * 2,
|
| 54 |
+
kernel_shp, 1, padding_shp
|
| 55 |
+
)
|
| 56 |
+
|
| 57 |
+
self.horiz_resid = nn.Conv2d(dim, dim, 1)
|
| 58 |
+
|
| 59 |
+
self.gate = GatedActivation()
|
| 60 |
+
|
| 61 |
+
def make_causal(self):
|
| 62 |
+
self.vert_stack.weight.data[:, :, -1].zero_() # Mask final row
|
| 63 |
+
self.horiz_stack.weight.data[:, :, :, -1].zero_() # Mask final column
|
| 64 |
+
|
| 65 |
+
def forward(self, x_v, x_h, h):
|
| 66 |
+
if self.mask_type == 'A':
|
| 67 |
+
self.make_causal()
|
| 68 |
+
|
| 69 |
+
h = self.class_cond_embedding(h)
|
| 70 |
+
h_vert = self.vert_stack(x_v)
|
| 71 |
+
h_vert = h_vert[:, :, :x_v.size(-1), :]
|
| 72 |
+
out_v = self.gate(h_vert + h[:, :, None, None])
|
| 73 |
+
|
| 74 |
+
h_horiz = self.horiz_stack(x_h)
|
| 75 |
+
h_horiz = h_horiz[:, :, :, :x_h.size(-2)]
|
| 76 |
+
v2h = self.vert_to_horiz(h_vert)
|
| 77 |
+
|
| 78 |
+
out = self.gate(v2h + h_horiz + h[:, :, None, None])
|
| 79 |
+
if self.residual:
|
| 80 |
+
out_h = self.horiz_resid(out) + x_h
|
| 81 |
+
else:
|
| 82 |
+
out_h = self.horiz_resid(out)
|
| 83 |
+
|
| 84 |
+
return out_v, out_h
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
class GatedPixelCNN(nn.Module):
|
| 88 |
+
def __init__(self, input_dim=256, dim=64, n_layers=15, n_classes=10):
|
| 89 |
+
super().__init__()
|
| 90 |
+
self.dim = dim
|
| 91 |
+
|
| 92 |
+
# Create embedding layer to embed input
|
| 93 |
+
self.embedding = nn.Embedding(input_dim, dim)
|
| 94 |
+
|
| 95 |
+
# Building the PixelCNN layer by layer
|
| 96 |
+
self.layers = nn.ModuleList()
|
| 97 |
+
|
| 98 |
+
# Initial block with Mask-A convolution
|
| 99 |
+
# Rest with Mask-B convolutions
|
| 100 |
+
for i in range(n_layers):
|
| 101 |
+
mask_type = 'A' if i == 0 else 'B'
|
| 102 |
+
kernel = 7 if i == 0 else 3
|
| 103 |
+
residual = False if i == 0 else True
|
| 104 |
+
|
| 105 |
+
self.layers.append(
|
| 106 |
+
GatedMaskedConv2d(mask_type, dim, kernel, residual, n_classes)
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
# Add the output layer
|
| 110 |
+
self.output_conv = nn.Sequential(
|
| 111 |
+
nn.Conv2d(dim, 512, 1),
|
| 112 |
+
nn.ReLU(True),
|
| 113 |
+
nn.Conv2d(512, input_dim, 1)
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
self.apply(weights_init)
|
| 117 |
+
|
| 118 |
+
def forward(self, x, label):
|
| 119 |
+
shp = x.size() + (-1, )
|
| 120 |
+
x = self.embedding(x.view(-1)).view(shp) # (B, H, W, C)
|
| 121 |
+
x = x.permute(0, 3, 1, 2) # (B, C, W, H)
|
| 122 |
+
|
| 123 |
+
x_v, x_h = (x, x)
|
| 124 |
+
for i, layer in enumerate(self.layers):
|
| 125 |
+
x_v, x_h = layer(x_v, x_h, label)
|
| 126 |
+
|
| 127 |
+
return self.output_conv(x_h)
|
| 128 |
+
|
| 129 |
+
def generate(self, label, shape=(8, 8), batch_size=64):
|
| 130 |
+
param = next(self.parameters())
|
| 131 |
+
x = torch.zeros(
|
| 132 |
+
(batch_size, *shape),
|
| 133 |
+
dtype=torch.int64, device=param.device
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
for i in range(shape[0]):
|
| 137 |
+
for j in range(shape[1]):
|
| 138 |
+
logits = self.forward(x, label)
|
| 139 |
+
probs = F.softmax(logits[:, :, i, j], -1)
|
| 140 |
+
x.data[:, i, j].copy_(
|
| 141 |
+
probs.multinomial(1).squeeze().data
|
| 142 |
+
)
|
| 143 |
+
return x
|
requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
huggingface_hub==0.22.2
|
| 2 |
+
torch
|
| 3 |
+
torchvision
|
util/__init__.py
ADDED
|
File without changes
|
util/html.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import dominate
|
| 2 |
+
from dominate.tags import *
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class HTML:
|
| 7 |
+
def __init__(self, web_dir, title, refresh=0):
|
| 8 |
+
self.title = title
|
| 9 |
+
self.web_dir = web_dir
|
| 10 |
+
self.img_dir = os.path.join(self.web_dir, 'images')
|
| 11 |
+
if not os.path.exists(self.web_dir):
|
| 12 |
+
os.makedirs(self.web_dir)
|
| 13 |
+
if not os.path.exists(self.img_dir):
|
| 14 |
+
os.makedirs(self.img_dir)
|
| 15 |
+
|
| 16 |
+
self.doc = dominate.document(title=title)
|
| 17 |
+
if refresh > 0:
|
| 18 |
+
with self.doc.head:
|
| 19 |
+
meta(http_equiv="refresh", content=str(refresh))
|
| 20 |
+
|
| 21 |
+
def get_image_dir(self):
|
| 22 |
+
return self.img_dir
|
| 23 |
+
|
| 24 |
+
def add_header(self, str):
|
| 25 |
+
with self.doc:
|
| 26 |
+
h3(str)
|
| 27 |
+
|
| 28 |
+
def add_table(self, border=1):
|
| 29 |
+
self.t = table(border=border, style="table-layout: fixed;")
|
| 30 |
+
self.doc.add(self.t)
|
| 31 |
+
|
| 32 |
+
def add_images(self, ims, txts, links, width=512):
|
| 33 |
+
self.add_table()
|
| 34 |
+
with self.t:
|
| 35 |
+
with tr():
|
| 36 |
+
for im, txt, link in zip(ims, txts, links):
|
| 37 |
+
with td(style="word-wrap: break-word;", halign="center", valign="top"):
|
| 38 |
+
with p():
|
| 39 |
+
with a(href=os.path.join('images', link)):
|
| 40 |
+
img(style="width:%dpx" % (width), src=os.path.join('images', im))
|
| 41 |
+
br()
|
| 42 |
+
p(txt)
|
| 43 |
+
|
| 44 |
+
def save(self):
|
| 45 |
+
html_file = '%s/index.html' % self.web_dir
|
| 46 |
+
f = open(html_file, 'wt')
|
| 47 |
+
f.write(self.doc.render())
|
| 48 |
+
f.close()
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
if __name__ == '__main__':
|
| 52 |
+
html = HTML('web/', 'test_html')
|
| 53 |
+
html.add_header('hello world')
|
| 54 |
+
|
| 55 |
+
ims = []
|
| 56 |
+
txts = []
|
| 57 |
+
links = []
|
| 58 |
+
for n in range(4):
|
| 59 |
+
ims.append('image_%d.jpg' % n)
|
| 60 |
+
txts.append('text_%d' % n)
|
| 61 |
+
links.append('image_%d.jpg' % n)
|
| 62 |
+
html.add_images(ims, txts, links)
|
| 63 |
+
html.save()
|
util/image_pool.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import random
|
| 2 |
+
import torch
|
| 3 |
+
from torch.autograd import Variable
|
| 4 |
+
class ImagePool():
|
| 5 |
+
def __init__(self, pool_size):
|
| 6 |
+
self.pool_size = pool_size
|
| 7 |
+
if self.pool_size > 0:
|
| 8 |
+
self.num_imgs = 0
|
| 9 |
+
self.images = []
|
| 10 |
+
|
| 11 |
+
def query(self, images):
|
| 12 |
+
if self.pool_size == 0:
|
| 13 |
+
return images
|
| 14 |
+
return_images = []
|
| 15 |
+
for image in images.data:
|
| 16 |
+
image = torch.unsqueeze(image, 0)
|
| 17 |
+
if self.num_imgs < self.pool_size:
|
| 18 |
+
self.num_imgs = self.num_imgs + 1
|
| 19 |
+
self.images.append(image)
|
| 20 |
+
return_images.append(image)
|
| 21 |
+
else:
|
| 22 |
+
p = random.uniform(0, 1)
|
| 23 |
+
if p > 0.5:
|
| 24 |
+
random_id = random.randint(0, self.pool_size-1)
|
| 25 |
+
tmp = self.images[random_id].clone()
|
| 26 |
+
self.images[random_id] = image
|
| 27 |
+
return_images.append(tmp)
|
| 28 |
+
else:
|
| 29 |
+
return_images.append(image)
|
| 30 |
+
return_images = Variable(torch.cat(return_images, 0))
|
| 31 |
+
return return_images
|
util/util.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import print_function
|
| 2 |
+
import torch
|
| 3 |
+
import numpy as np
|
| 4 |
+
from PIL import Image
|
| 5 |
+
import numpy as np
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
# Converts a Tensor into a Numpy array
|
| 9 |
+
# |imtype|: the desired type of the converted numpy array
|
| 10 |
+
def tensor2im(image_tensor, imtype=np.uint8, normalize=True):
|
| 11 |
+
if isinstance(image_tensor, list):
|
| 12 |
+
image_numpy = []
|
| 13 |
+
for i in range(len(image_tensor)):
|
| 14 |
+
image_numpy.append(tensor2im(image_tensor[i], imtype, normalize))
|
| 15 |
+
return image_numpy
|
| 16 |
+
image_numpy = image_tensor.cpu().float().numpy()
|
| 17 |
+
if normalize:
|
| 18 |
+
image_numpy = (np.transpose(image_numpy, (1, 2, 0)) + 1) / 2.0 * 255.0
|
| 19 |
+
else:
|
| 20 |
+
image_numpy = np.transpose(image_numpy, (1, 2, 0)) * 255.0
|
| 21 |
+
image_numpy = np.clip(image_numpy, 0, 255)
|
| 22 |
+
if image_numpy.shape[2] == 1 or image_numpy.shape[2] > 3:
|
| 23 |
+
image_numpy = image_numpy[:,:,0]
|
| 24 |
+
return image_numpy.astype(imtype)
|
| 25 |
+
|
| 26 |
+
# Converts a one-hot tensor into a colorful label map
|
| 27 |
+
def tensor2label(label_tensor, n_label, imtype=np.uint8):
|
| 28 |
+
if n_label == 0:
|
| 29 |
+
return tensor2im(label_tensor, imtype)
|
| 30 |
+
label_tensor = label_tensor.cpu().float()
|
| 31 |
+
if label_tensor.size()[0] > 1:
|
| 32 |
+
label_tensor = label_tensor.max(0, keepdim=True)[1]
|
| 33 |
+
label_tensor = Colorize(n_label)(label_tensor)
|
| 34 |
+
label_numpy = np.transpose(label_tensor.numpy(), (1, 2, 0))
|
| 35 |
+
return label_numpy.astype(imtype)
|
| 36 |
+
|
| 37 |
+
def save_image(image_numpy, image_path):
|
| 38 |
+
image_pil = Image.fromarray(image_numpy)
|
| 39 |
+
image_pil.save(image_path)
|
| 40 |
+
|
| 41 |
+
def mkdirs(paths):
|
| 42 |
+
if isinstance(paths, list) and not isinstance(paths, str):
|
| 43 |
+
for path in paths:
|
| 44 |
+
mkdir(path)
|
| 45 |
+
else:
|
| 46 |
+
mkdir(paths)
|
| 47 |
+
|
| 48 |
+
def mkdir(path):
|
| 49 |
+
if not os.path.exists(path):
|
| 50 |
+
os.makedirs(path)
|
| 51 |
+
|
| 52 |
+
###############################################################################
|
| 53 |
+
# Code from
|
| 54 |
+
# https://github.com/ycszen/pytorch-seg/blob/master/transform.py
|
| 55 |
+
# Modified so it complies with the Citscape label map colors
|
| 56 |
+
###############################################################################
|
| 57 |
+
def uint82bin(n, count=8):
|
| 58 |
+
"""returns the binary of integer n, count refers to amount of bits"""
|
| 59 |
+
return ''.join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
|
| 60 |
+
|
| 61 |
+
def labelcolormap(N):
|
| 62 |
+
if N == 35: # cityscape
|
| 63 |
+
cmap = np.array([( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), (111, 74, 0), ( 81, 0, 81),
|
| 64 |
+
(128, 64,128), (244, 35,232), (250,170,160), (230,150,140), ( 70, 70, 70), (102,102,156), (190,153,153),
|
| 65 |
+
(180,165,180), (150,100,100), (150,120, 90), (153,153,153), (153,153,153), (250,170, 30), (220,220, 0),
|
| 66 |
+
(107,142, 35), (152,251,152), ( 70,130,180), (220, 20, 60), (255, 0, 0), ( 0, 0,142), ( 0, 0, 70),
|
| 67 |
+
( 0, 60,100), ( 0, 0, 90), ( 0, 0,110), ( 0, 80,100), ( 0, 0,230), (119, 11, 32), ( 0, 0,142)],
|
| 68 |
+
dtype=np.uint8)
|
| 69 |
+
else:
|
| 70 |
+
cmap = np.zeros((N, 3), dtype=np.uint8)
|
| 71 |
+
for i in range(N):
|
| 72 |
+
r, g, b = 0, 0, 0
|
| 73 |
+
id = i
|
| 74 |
+
for j in range(7):
|
| 75 |
+
str_id = uint82bin(id)
|
| 76 |
+
r = r ^ (np.uint8(str_id[-1]) << (7-j))
|
| 77 |
+
g = g ^ (np.uint8(str_id[-2]) << (7-j))
|
| 78 |
+
b = b ^ (np.uint8(str_id[-3]) << (7-j))
|
| 79 |
+
id = id >> 3
|
| 80 |
+
cmap[i, 0] = r
|
| 81 |
+
cmap[i, 1] = g
|
| 82 |
+
cmap[i, 2] = b
|
| 83 |
+
return cmap
|
| 84 |
+
|
| 85 |
+
class Colorize(object):
|
| 86 |
+
def __init__(self, n=35):
|
| 87 |
+
self.cmap = labelcolormap(n)
|
| 88 |
+
self.cmap = torch.from_numpy(self.cmap[:n])
|
| 89 |
+
|
| 90 |
+
def __call__(self, gray_image):
|
| 91 |
+
size = gray_image.size()
|
| 92 |
+
color_image = torch.ByteTensor(3, size[1], size[2]).fill_(0)
|
| 93 |
+
|
| 94 |
+
for label in range(0, len(self.cmap)):
|
| 95 |
+
mask = (label == gray_image[0]).cpu()
|
| 96 |
+
color_image[0][mask] = self.cmap[label][0]
|
| 97 |
+
color_image[1][mask] = self.cmap[label][1]
|
| 98 |
+
color_image[2][mask] = self.cmap[label][2]
|
| 99 |
+
|
| 100 |
+
return color_image
|
util/visualizer.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import os
|
| 3 |
+
import ntpath
|
| 4 |
+
import time
|
| 5 |
+
from . import util
|
| 6 |
+
from . import html
|
| 7 |
+
import scipy.misc
|
| 8 |
+
try:
|
| 9 |
+
from StringIO import StringIO # Python 2.7
|
| 10 |
+
except ImportError:
|
| 11 |
+
from io import BytesIO # Python 3.x
|
| 12 |
+
|
| 13 |
+
class Visualizer():
|
| 14 |
+
def __init__(self, opt):
|
| 15 |
+
# self.opt = opt
|
| 16 |
+
self.tf_log = opt.tf_log
|
| 17 |
+
self.use_html = opt.isTrain and not opt.no_html
|
| 18 |
+
self.win_size = opt.display_winsize
|
| 19 |
+
self.name = opt.name
|
| 20 |
+
if self.tf_log:
|
| 21 |
+
import tensorflow as tf
|
| 22 |
+
self.tf = tf
|
| 23 |
+
self.log_dir = os.path.join(opt.checkpoints_dir, opt.name, 'logs')
|
| 24 |
+
self.writer = tf.summary.FileWriter(self.log_dir)
|
| 25 |
+
|
| 26 |
+
if self.use_html:
|
| 27 |
+
self.web_dir = os.path.join(opt.checkpoints_dir, opt.name, 'web')
|
| 28 |
+
self.img_dir = os.path.join(self.web_dir, 'images')
|
| 29 |
+
print('create web directory %s...' % self.web_dir)
|
| 30 |
+
util.mkdirs([self.web_dir, self.img_dir])
|
| 31 |
+
self.log_name = os.path.join(opt.checkpoints_dir, opt.name, 'loss_log.txt')
|
| 32 |
+
with open(self.log_name, "a") as log_file:
|
| 33 |
+
now = time.strftime("%c")
|
| 34 |
+
log_file.write('================ Training Loss (%s) ================\n' % now)
|
| 35 |
+
|
| 36 |
+
# |visuals|: dictionary of images to display or save
|
| 37 |
+
def display_current_results(self, visuals, epoch, step):
|
| 38 |
+
if self.tf_log: # show images in tensorboard output
|
| 39 |
+
img_summaries = []
|
| 40 |
+
for label, image_numpy in visuals.items():
|
| 41 |
+
# Write the image to a string
|
| 42 |
+
try:
|
| 43 |
+
s = StringIO()
|
| 44 |
+
except:
|
| 45 |
+
s = BytesIO()
|
| 46 |
+
scipy.misc.toimage(image_numpy).save(s, format="jpeg")
|
| 47 |
+
# Create an Image object
|
| 48 |
+
img_sum = self.tf.Summary.Image(encoded_image_string=s.getvalue(), height=image_numpy.shape[0], width=image_numpy.shape[1])
|
| 49 |
+
# Create a Summary value
|
| 50 |
+
img_summaries.append(self.tf.Summary.Value(tag=label, image=img_sum))
|
| 51 |
+
|
| 52 |
+
# Create and write Summary
|
| 53 |
+
summary = self.tf.Summary(value=img_summaries)
|
| 54 |
+
self.writer.add_summary(summary, step)
|
| 55 |
+
|
| 56 |
+
if self.use_html: # save images to a html file
|
| 57 |
+
for label, image_numpy in visuals.items():
|
| 58 |
+
if isinstance(image_numpy, list):
|
| 59 |
+
for i in range(len(image_numpy)):
|
| 60 |
+
img_path = os.path.join(self.img_dir, 'epoch%.3d_%s_%d.jpg' % (epoch, label, i))
|
| 61 |
+
util.save_image(image_numpy[i], img_path)
|
| 62 |
+
else:
|
| 63 |
+
img_path = os.path.join(self.img_dir, 'epoch%.3d_%s.jpg' % (epoch, label))
|
| 64 |
+
util.save_image(image_numpy, img_path)
|
| 65 |
+
|
| 66 |
+
# update website
|
| 67 |
+
webpage = html.HTML(self.web_dir, 'Experiment name = %s' % self.name, refresh=30)
|
| 68 |
+
for n in range(epoch, 0, -1):
|
| 69 |
+
webpage.add_header('epoch [%d]' % n)
|
| 70 |
+
ims = []
|
| 71 |
+
txts = []
|
| 72 |
+
links = []
|
| 73 |
+
|
| 74 |
+
for label, image_numpy in visuals.items():
|
| 75 |
+
if isinstance(image_numpy, list):
|
| 76 |
+
for i in range(len(image_numpy)):
|
| 77 |
+
img_path = 'epoch%.3d_%s_%d.jpg' % (n, label, i)
|
| 78 |
+
ims.append(img_path)
|
| 79 |
+
txts.append(label+str(i))
|
| 80 |
+
links.append(img_path)
|
| 81 |
+
else:
|
| 82 |
+
img_path = 'epoch%.3d_%s.jpg' % (n, label)
|
| 83 |
+
ims.append(img_path)
|
| 84 |
+
txts.append(label)
|
| 85 |
+
links.append(img_path)
|
| 86 |
+
if len(ims) < 10:
|
| 87 |
+
webpage.add_images(ims, txts, links, width=self.win_size)
|
| 88 |
+
else:
|
| 89 |
+
num = int(round(len(ims)/2.0))
|
| 90 |
+
webpage.add_images(ims[:num], txts[:num], links[:num], width=self.win_size)
|
| 91 |
+
webpage.add_images(ims[num:], txts[num:], links[num:], width=self.win_size)
|
| 92 |
+
webpage.save()
|
| 93 |
+
|
| 94 |
+
# errors: dictionary of error labels and values
|
| 95 |
+
def plot_current_errors(self, errors, step):
|
| 96 |
+
if self.tf_log:
|
| 97 |
+
for tag, value in errors.items():
|
| 98 |
+
summary = self.tf.Summary(value=[self.tf.Summary.Value(tag=tag, simple_value=value)])
|
| 99 |
+
self.writer.add_summary(summary, step)
|
| 100 |
+
|
| 101 |
+
# errors: same format as |errors| of plotCurrentErrors
|
| 102 |
+
def print_current_errors(self, epoch, i, errors, t):
|
| 103 |
+
message = '(epoch: %d, iters: %d, time: %.3f) ' % (epoch, i, t)
|
| 104 |
+
for k, v in errors.items():
|
| 105 |
+
if v != 0:
|
| 106 |
+
message += '%s: %.3f ' % (k, v)
|
| 107 |
+
|
| 108 |
+
print(message)
|
| 109 |
+
with open(self.log_name, "a") as log_file:
|
| 110 |
+
log_file.write('%s\n' % message)
|
| 111 |
+
|
| 112 |
+
# save image to the disk
|
| 113 |
+
def save_images(self, webpage, visuals, image_path):
|
| 114 |
+
image_dir = webpage.get_image_dir()
|
| 115 |
+
short_path = ntpath.basename(image_path[0])
|
| 116 |
+
name = os.path.splitext(short_path)[0]
|
| 117 |
+
|
| 118 |
+
webpage.add_header(name)
|
| 119 |
+
ims = []
|
| 120 |
+
txts = []
|
| 121 |
+
links = []
|
| 122 |
+
|
| 123 |
+
for label, image_numpy in visuals.items():
|
| 124 |
+
image_name = '%s_%s.jpg' % (name, label)
|
| 125 |
+
save_path = os.path.join(image_dir, image_name)
|
| 126 |
+
util.save_image(image_numpy, save_path)
|
| 127 |
+
|
| 128 |
+
ims.append(image_name)
|
| 129 |
+
txts.append(label)
|
| 130 |
+
links.append(image_name)
|
| 131 |
+
webpage.add_images(ims, txts, links, width=self.win_size)
|