sample_diffusion.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. import argparse, os, sys, glob, datetime, yaml
  2. import torch
  3. import time
  4. import numpy as np
  5. from tqdm import trange
  6. from omegaconf import OmegaConf
  7. from PIL import Image
  8. from ldm.models.diffusion.ddim import DDIMSampler
  9. from ldm.util import instantiate_from_config
  10. rescale = lambda x: (x + 1.) / 2.
  11. def custom_to_pil(x):
  12. x = x.detach().cpu()
  13. x = torch.clamp(x, -1., 1.)
  14. x = (x + 1.) / 2.
  15. x = x.permute(1, 2, 0).numpy()
  16. x = (255 * x).astype(np.uint8)
  17. x = Image.fromarray(x)
  18. if not x.mode == "RGB":
  19. x = x.convert("RGB")
  20. return x
  21. def custom_to_np(x):
  22. # saves the batch in adm style as in https://github.com/openai/guided-diffusion/blob/main/scripts/image_sample.py
  23. sample = x.detach().cpu()
  24. sample = ((sample + 1) * 127.5).clamp(0, 255).to(torch.uint8)
  25. sample = sample.permute(0, 2, 3, 1)
  26. sample = sample.contiguous()
  27. return sample
  28. def logs2pil(logs, keys=["sample"]):
  29. imgs = dict()
  30. for k in logs:
  31. try:
  32. if len(logs[k].shape) == 4:
  33. img = custom_to_pil(logs[k][0, ...])
  34. elif len(logs[k].shape) == 3:
  35. img = custom_to_pil(logs[k])
  36. else:
  37. print(f"Unknown format for key {k}. ")
  38. img = None
  39. except:
  40. img = None
  41. imgs[k] = img
  42. return imgs
  43. @torch.no_grad()
  44. def convsample(model, shape, return_intermediates=True,
  45. verbose=True,
  46. make_prog_row=False):
  47. if not make_prog_row:
  48. return model.p_sample_loop(None, shape,
  49. return_intermediates=return_intermediates, verbose=verbose)
  50. else:
  51. return model.progressive_denoising(
  52. None, shape, verbose=True
  53. )
  54. @torch.no_grad()
  55. def convsample_ddim(model, steps, shape, eta=1.0
  56. ):
  57. ddim = DDIMSampler(model)
  58. bs = shape[0]
  59. shape = shape[1:]
  60. samples, intermediates = ddim.sample(steps, batch_size=bs, shape=shape, eta=eta, verbose=False,)
  61. return samples, intermediates
  62. @torch.no_grad()
  63. def make_convolutional_sample(model, batch_size, vanilla=False, custom_steps=None, eta=1.0,):
  64. log = dict()
  65. shape = [batch_size,
  66. model.model.diffusion_model.in_channels,
  67. model.model.diffusion_model.image_size,
  68. model.model.diffusion_model.image_size]
  69. with model.ema_scope("Plotting"):
  70. t0 = time.time()
  71. if vanilla:
  72. sample, progrow = convsample(model, shape,
  73. make_prog_row=True)
  74. else:
  75. sample, intermediates = convsample_ddim(model, steps=custom_steps, shape=shape,
  76. eta=eta)
  77. t1 = time.time()
  78. x_sample = model.decode_first_stage(sample)
  79. log["sample"] = x_sample
  80. log["time"] = t1 - t0
  81. log['throughput'] = sample.shape[0] / (t1 - t0)
  82. print(f'Throughput for this batch: {log["throughput"]}')
  83. return log
  84. def run(model, logdir, batch_size=50, vanilla=False, custom_steps=None, eta=None, n_samples=50000, nplog=None):
  85. if vanilla:
  86. print(f'Using Vanilla DDPM sampling with {model.num_timesteps} sampling steps.')
  87. else:
  88. print(f'Using DDIM sampling with {custom_steps} sampling steps and eta={eta}')
  89. tstart = time.time()
  90. n_saved = len(glob.glob(os.path.join(logdir,'*.png')))-1
  91. # path = logdir
  92. if model.cond_stage_model is None:
  93. all_images = []
  94. print(f"Running unconditional sampling for {n_samples} samples")
  95. for _ in trange(n_samples // batch_size, desc="Sampling Batches (unconditional)"):
  96. logs = make_convolutional_sample(model, batch_size=batch_size,
  97. vanilla=vanilla, custom_steps=custom_steps,
  98. eta=eta)
  99. n_saved = save_logs(logs, logdir, n_saved=n_saved, key="sample")
  100. all_images.extend([custom_to_np(logs["sample"])])
  101. if n_saved >= n_samples:
  102. print(f'Finish after generating {n_saved} samples')
  103. break
  104. all_img = np.concatenate(all_images, axis=0)
  105. all_img = all_img[:n_samples]
  106. shape_str = "x".join([str(x) for x in all_img.shape])
  107. nppath = os.path.join(nplog, f"{shape_str}-samples.npz")
  108. np.savez(nppath, all_img)
  109. else:
  110. raise NotImplementedError('Currently only sampling for unconditional models supported.')
  111. print(f"sampling of {n_saved} images finished in {(time.time() - tstart) / 60.:.2f} minutes.")
  112. def save_logs(logs, path, n_saved=0, key="sample", np_path=None):
  113. for k in logs:
  114. if k == key:
  115. batch = logs[key]
  116. if np_path is None:
  117. for x in batch:
  118. img = custom_to_pil(x)
  119. imgpath = os.path.join(path, f"{key}_{n_saved:06}.png")
  120. img.save(imgpath)
  121. n_saved += 1
  122. else:
  123. npbatch = custom_to_np(batch)
  124. shape_str = "x".join([str(x) for x in npbatch.shape])
  125. nppath = os.path.join(np_path, f"{n_saved}-{shape_str}-samples.npz")
  126. np.savez(nppath, npbatch)
  127. n_saved += npbatch.shape[0]
  128. return n_saved
  129. def get_parser():
  130. parser = argparse.ArgumentParser()
  131. parser.add_argument(
  132. "-r",
  133. "--resume",
  134. type=str,
  135. nargs="?",
  136. help="load from logdir or checkpoint in logdir",
  137. )
  138. parser.add_argument(
  139. "-n",
  140. "--n_samples",
  141. type=int,
  142. nargs="?",
  143. help="number of samples to draw",
  144. default=50000
  145. )
  146. parser.add_argument(
  147. "-e",
  148. "--eta",
  149. type=float,
  150. nargs="?",
  151. help="eta for ddim sampling (0.0 yields deterministic sampling)",
  152. default=1.0
  153. )
  154. parser.add_argument(
  155. "-v",
  156. "--vanilla_sample",
  157. default=False,
  158. action='store_true',
  159. help="vanilla sampling (default option is DDIM sampling)?",
  160. )
  161. parser.add_argument(
  162. "-l",
  163. "--logdir",
  164. type=str,
  165. nargs="?",
  166. help="extra logdir",
  167. default="none"
  168. )
  169. parser.add_argument(
  170. "-c",
  171. "--custom_steps",
  172. type=int,
  173. nargs="?",
  174. help="number of steps for ddim and fastdpm sampling",
  175. default=50
  176. )
  177. parser.add_argument(
  178. "--batch_size",
  179. type=int,
  180. nargs="?",
  181. help="the bs",
  182. default=10
  183. )
  184. return parser
  185. def load_model_from_config(config, sd):
  186. model = instantiate_from_config(config)
  187. model.load_state_dict(sd,strict=False)
  188. model.cuda()
  189. model.eval()
  190. return model
  191. def load_model(config, ckpt, gpu, eval_mode):
  192. if ckpt:
  193. print(f"Loading model from {ckpt}")
  194. pl_sd = torch.load(ckpt, map_location="cpu")
  195. global_step = pl_sd["global_step"]
  196. else:
  197. pl_sd = {"state_dict": None}
  198. global_step = None
  199. model = load_model_from_config(config.model,
  200. pl_sd["state_dict"])
  201. return model, global_step
  202. if __name__ == "__main__":
  203. now = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
  204. sys.path.append(os.getcwd())
  205. command = " ".join(sys.argv)
  206. parser = get_parser()
  207. opt, unknown = parser.parse_known_args()
  208. ckpt = None
  209. if not os.path.exists(opt.resume):
  210. raise ValueError("Cannot find {}".format(opt.resume))
  211. if os.path.isfile(opt.resume):
  212. # paths = opt.resume.split("/")
  213. try:
  214. logdir = '/'.join(opt.resume.split('/')[:-1])
  215. # idx = len(paths)-paths[::-1].index("logs")+1
  216. print(f'Logdir is {logdir}')
  217. except ValueError:
  218. paths = opt.resume.split("/")
  219. idx = -2 # take a guess: path/to/logdir/checkpoints/model.ckpt
  220. logdir = "/".join(paths[:idx])
  221. ckpt = opt.resume
  222. else:
  223. assert os.path.isdir(opt.resume), f"{opt.resume} is not a directory"
  224. logdir = opt.resume.rstrip("/")
  225. ckpt = os.path.join(logdir, "model.ckpt")
  226. base_configs = sorted(glob.glob(os.path.join(logdir, "config.yaml")))
  227. opt.base = base_configs
  228. configs = [OmegaConf.load(cfg) for cfg in opt.base]
  229. cli = OmegaConf.from_dotlist(unknown)
  230. config = OmegaConf.merge(*configs, cli)
  231. gpu = True
  232. eval_mode = True
  233. if opt.logdir != "none":
  234. locallog = logdir.split(os.sep)[-1]
  235. if locallog == "": locallog = logdir.split(os.sep)[-2]
  236. print(f"Switching logdir from '{logdir}' to '{os.path.join(opt.logdir, locallog)}'")
  237. logdir = os.path.join(opt.logdir, locallog)
  238. print(config)
  239. model, global_step = load_model(config, ckpt, gpu, eval_mode)
  240. print(f"global step: {global_step}")
  241. print(75 * "=")
  242. print("logging to:")
  243. logdir = os.path.join(logdir, "samples", f"{global_step:08}", now)
  244. imglogdir = os.path.join(logdir, "img")
  245. numpylogdir = os.path.join(logdir, "numpy")
  246. os.makedirs(imglogdir)
  247. os.makedirs(numpylogdir)
  248. print(logdir)
  249. print(75 * "=")
  250. # write config out
  251. sampling_file = os.path.join(logdir, "sampling_config.yaml")
  252. sampling_conf = vars(opt)
  253. with open(sampling_file, 'w') as f:
  254. yaml.dump(sampling_conf, f, default_flow_style=False)
  255. print(sampling_conf)
  256. run(model, imglogdir, eta=opt.eta,
  257. vanilla=opt.vanilla_sample, n_samples=opt.n_samples, custom_steps=opt.custom_steps,
  258. batch_size=opt.batch_size, nplog=numpylogdir)
  259. print("done.")