Update
[pytorch] / hallu.py
1 #!/usr/bin/env python
2
3 # ImageMagick's montage to make the mosaic
4 #
5 # montage hallu-*.png -tile 5x6 -geometry +1+1 result.png
6
7 import PIL, torch, torchvision
8 from torch.nn import functional as F
9
10 class MultiScaleEdgeEnergy(torch.nn.Module):
11     def __init__(self):
12         super(MultiScaleEdgeEnergy, self).__init__()
13         k = torch.exp(- torch.tensor([[-2., -1., 0., 1., 2.]])**2 / 2)
14         k = (k.t() @ k).view(1, 1, 5, 5)
15         self.gaussian_5x5 = torch.nn.Parameter(k / k.sum()).requires_grad_(False)
16
17     def forward(self, x):
18         u = x.view(-1, 1, x.size(2), x.size(3))
19         result = 0.0
20         while min(u.size(2), u.size(3)) > 5:
21             blurry  = F.conv2d(u, self.gaussian_5x5, padding = 2)
22             result += (u - blurry).view(u.size(0), -1).pow(2).sum(1)
23             u = F.avg_pool2d(u, kernel_size = 2, padding = 1)
24         return result.view(x.size(0), -1).sum(1)
25
26 img = torchvision.transforms.ToTensor()(PIL.Image.open('blacklab.jpg'))
27 img = img.view((1,) + img.size())
28 ref_input = 0.5 + 0.5 * (img - img.mean()) / img.std()
29
30 mse_loss = torch.nn.MSELoss()
31 edge_energy = MultiScaleEdgeEnergy()
32
33 layers = torchvision.models.vgg16(pretrained = True).features
34 layers.eval()
35
36 if torch.cuda.is_available():
37     edge_energy.cuda()
38     ref_input = ref_input.cuda()
39     layers.cuda()
40
41 for l in [ 5, 7, 12, 17, 21, 28 ]:
42     model = torch.nn.Sequential(layers[:l])
43     ref_output = model(ref_input).detach()
44
45     for n in range(5):
46         input = torch.empty_like(ref_input).uniform_(-0.01, 0.01).requires_grad_()
47         optimizer = torch.optim.Adam( [ input ], lr = 1e-2)
48         for k in range(1000):
49             output = model(input)
50             loss = mse_loss(output, ref_output) + 1e-3 * edge_energy(input)
51             optimizer.zero_grad()
52             loss.backward()
53             optimizer.step()
54
55         img = 0.5 + 0.2 * (input - input.mean()) / input.std()
56         result_name = 'hallu-l%02d-n%02d.png' % (l, n)
57         torchvision.utils.save_image(img, result_name)
58
59         print('Wrote ' + result_name)