Legola commited on
Commit
666de43
·
1 Parent(s): aa1bc3b

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +140 -0
  2. model.py +135 -0
app.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import print_function
2
+ import torch
3
+ import torchvision
4
+ import torch.nn as nn
5
+ import gradio as gr
6
+ import os
7
+ import time
8
+ import torch.nn.functional as F
9
+ import torch.optim as optim
10
+ import matplotlib.pyplot as plt
11
+ import torchvision.transforms as transforms
12
+ import copy
13
+ import torchvision.models as models
14
+ import torchvision.transforms.functional as TF
15
+ from PIL import Image
16
+ import numpy as np
17
+ from model import ContentLoss, gram_matrix, StyleLoss, image_transform, get_input_optimizer,get_style_model_and_losses
18
+
19
+
20
+
21
+
22
+
23
+ ## style_transfer
24
+ import numpy as np
25
+ def run_style_transfer(cnn, normalization_mean, normalization_std,
26
+ content_img, style_img, input_img, num_steps=300,
27
+ style_weight=1000000, content_weight=1):
28
+ """Run the style transfer."""
29
+ print('Building the style transfer model..')
30
+ model, style_losses, content_losses = get_style_model_and_losses(cnn,
31
+ normalization_mean, normalization_std, style_img, content_img)
32
+
33
+ # We want to optimize the input and not the model parameters so we
34
+ # update all the requires_grad fields accordingly
35
+ input_img.requires_grad_(True)
36
+ model.requires_grad_(False)
37
+
38
+ optimizer = get_input_optimizer(input_img)
39
+
40
+ print('Optimizing..')
41
+ run = [0]
42
+ while run[0] <= num_steps:
43
+
44
+ def closure():
45
+ # correct the values of updated input image
46
+ with torch.no_grad():
47
+ input_img.clamp_(0, 1)
48
+
49
+ optimizer.zero_grad()
50
+ model(input_img)
51
+ style_score = 0
52
+ content_score = 0
53
+
54
+ for sl in style_losses:
55
+ style_score += sl.loss
56
+ for cl in content_losses:
57
+ content_score += cl.loss
58
+
59
+ style_score *= style_weight
60
+ content_score *= content_weight
61
+
62
+ loss = style_score + content_score
63
+ loss.backward()
64
+
65
+ run[0] += 1
66
+ if run[0] % 50 == 0:
67
+ print("run {}:".format(run))
68
+ print('Style Loss : {:4f} Content Loss: {:4f}'.format(
69
+ style_score.item(), content_score.item()))
70
+ print()
71
+
72
+ return style_score + content_score
73
+
74
+ optimizer.step(closure)
75
+
76
+ # a last correction...
77
+ with torch.no_grad():
78
+ input_img.clamp_(0, 1)
79
+
80
+ # Convert output tensor to a NumPy array
81
+ output_np = input_img.detach().cpu().numpy()[0].transpose(1, 2, 0)
82
+
83
+ # Convert NumPy array to PIL Image object
84
+ output_img = Image.fromarray((output_np * 255).astype(np.uint8))
85
+
86
+ return output_img
87
+
88
+
89
+ #Defining the predict function
90
+ def style_transfer(cont_img,styl_img):
91
+
92
+ #Start the timer
93
+ start_time = time.time()
94
+
95
+
96
+ #transform the input image
97
+ style_img = image_transform(styl_img)
98
+ content_img =image_transform(cont_img)
99
+
100
+ #getting input image
101
+ input_img = content_img.clone()
102
+
103
+ #running the style transfer
104
+ output = run_style_transfer(cnn, cnn_normalization_mean, cnn_normalization_std,
105
+ content_img, style_img, input_img)
106
+ # output_img = output.detach().cpu().squeeze(0)
107
+ # output_img = TF.to_pil_image(output_img)
108
+ end_time=time.time()
109
+
110
+ pred_time =round(end_time- start_time, 5)
111
+
112
+ return output
113
+
114
+ ##Gradio App
115
+ import gradio as gr
116
+ title= 'Style Transfer'
117
+ description='A model to transfer the style of one image to another'
118
+ article = 'Created at Pytorch Model Deployment'
119
+
120
+ #example_images
121
+ example_list = [["examples/" + example] for example in os.listdir("examples")]
122
+
123
+ #Create the gradio demo
124
+ demo = gr.Interface(
125
+ fn=style_transfer,
126
+ inputs=[
127
+ gr.inputs.Image(label="content Image",type='pil'),
128
+ gr.inputs.Image(label="style_image",type='pil')
129
+ ],
130
+ examples=example_list,
131
+ outputs="image",
132
+ allow_flagging=False,
133
+ title=title,
134
+ description=description,
135
+ article=article
136
+ )
137
+
138
+ # Launch the Gradio interface
139
+ demo.launch(debug=True)
140
+
model.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ #Content Loss
3
+ class ContentLoss(nn.Module):
4
+
5
+ def __init__(self, target,):
6
+ super(ContentLoss, self).__init__()
7
+ '''
8
+ we 'detach' the target content from the tree used
9
+ to dynamically compute the gradient: this is a stated value,
10
+ not a variable. Otherwise the forward method of the criterion
11
+ will throw an error.
12
+ '''
13
+ self.target = target.detach()
14
+
15
+ def forward(self, input):
16
+ self.loss = F.mse_loss(input, self.target)
17
+ return input
18
+
19
+ #Style Loss
20
+ def gram_matrix(input):
21
+ a, b, c, d = input.size() # a=batch size(=1)
22
+ # b=number of feature maps
23
+ # (c,d)=dimensions of a f. map (N=c*d)
24
+
25
+ features = input.view(a * b, c * d) # resize F_XL into \hat F_XL
26
+
27
+ G = torch.mm(features, features.t()) # compute the gram product
28
+
29
+ # we 'normalize' the values of the gram matrix
30
+ # by dividing by the number of element in each feature maps.
31
+ return G.div(a * b * c * d)
32
+
33
+ class StyleLoss(nn.Module):
34
+
35
+ def __init__(self, target_feature):
36
+ super(StyleLoss, self).__init__()
37
+ self.target = gram_matrix(target_feature).detach()
38
+
39
+ def forward(self, input):
40
+ G = gram_matrix(input)
41
+ self.loss = F.mse_loss(G, self.target)
42
+ return input
43
+
44
+
45
+ #Normalization
46
+
47
+ cnn_normalization_mean = torch.tensor([0.485, 0.456, 0.406]).to(device)
48
+ cnn_normalization_std = torch.tensor([0.229, 0.224, 0.225]).to(device)
49
+ #image transformation
50
+ def image_transform(image):
51
+ if isinstance(image, str):
52
+ # If image is a path to a file, open it using PIL
53
+ image = Image.open(image).convert('RGB')
54
+ else:
55
+ # If image is a NumPy array, convert it to a PIL image
56
+ image = Image.fromarray(image.astype('uint8'), 'RGB')
57
+ # Apply the same transformations as before
58
+ image = transform(image).unsqueeze(0)
59
+ return image.to(device)
60
+
61
+
62
+
63
+ #Defining a model
64
+ cnn = models.vgg19(pretrained=True).features.to(device).eval()
65
+
66
+ #getting the input optimizer
67
+ def get_input_optimizer(input_img):
68
+ # this line to show that input is a parameter that requires a gradient
69
+ optimizer = optim.LBFGS([input_img])
70
+ return optimizer
71
+
72
+ # desired depth layers to compute style/content losses :
73
+
74
+
75
+ content_layers_default = ['conv_4']
76
+ style_layers_default = ['conv_1', 'conv_2', 'conv_3', 'conv_4', 'conv_5']
77
+
78
+ def get_style_model_and_losses(cnn, normalization_mean, normalization_std,
79
+ style_img, content_img,
80
+ content_layers=content_layers_default,
81
+ style_layers=style_layers_default):
82
+ # normalization module
83
+ normalization = Normalization(normalization_mean, normalization_std).to(device)
84
+
85
+ # just in order to have an iterable access to or list of content/style
86
+ # losses
87
+ content_losses = []
88
+ style_losses = []
89
+
90
+ # assuming that ``cnn`` is a ``nn.Sequential``, so we make a new ``nn.Sequential``
91
+ # to put in modules that are supposed to be activated sequentially
92
+ model = nn.Sequential(normalization)
93
+
94
+ i = 0 # increment every time we see a conv
95
+ for layer in cnn.children():
96
+ if isinstance(layer, nn.Conv2d):
97
+ i += 1
98
+ name = 'conv_{}'.format(i)
99
+ elif isinstance(layer, nn.ReLU):
100
+ name = 'relu_{}'.format(i)
101
+ # The in-place version doesn't play very nicely with the ``ContentLoss``
102
+ # and ``StyleLoss`` we insert below. So we replace with out-of-place
103
+ # ones here.
104
+ layer = nn.ReLU(inplace=False)
105
+ elif isinstance(layer, nn.MaxPool2d):
106
+ name = 'pool_{}'.format(i)
107
+ elif isinstance(layer, nn.BatchNorm2d):
108
+ name = 'bn_{}'.format(i)
109
+ else:
110
+ raise RuntimeError('Unrecognized layer: {}'.format(layer.__class__.__name__))
111
+
112
+ model.add_module(name, layer)
113
+
114
+ if name in content_layers:
115
+ # add content loss:
116
+ target = model(content_img).detach()
117
+ content_loss = ContentLoss(target)
118
+ model.add_module("content_loss_{}".format(i), content_loss)
119
+ content_losses.append(content_loss)
120
+
121
+ if name in style_layers:
122
+ # add style loss:
123
+ target_feature = model(style_img).detach()
124
+ style_loss = StyleLoss(target_feature)
125
+ model.add_module("style_loss_{}".format(i), style_loss)
126
+ style_losses.append(style_loss)
127
+
128
+ # now we trim off the layers after the last content and style losses
129
+ for i in range(len(model) - 1, -1, -1):
130
+ if isinstance(model[i], ContentLoss) or isinstance(model[i], StyleLoss):
131
+ break
132
+
133
+ model = model[:(i + 1)]
134
+
135
+ return model, style_losses, content_losses