Tim Zhang commited on
Commit
b8d244d
·
1 Parent(s): fbfaa18

inference added

Browse files
Files changed (2) hide show
  1. app.py +8 -15
  2. clip_styler.py +14 -105
app.py CHANGED
@@ -67,19 +67,19 @@ tf_edit = transforms.Compose([
67
  transforms.ToTensor()
68
  ])
69
 
70
- def edit_image(image, style_choice, num_steps):
71
  if image is None: return None
72
 
73
- weights_map = {
74
- "Sketch": "model_sketch.pth",
75
- "Van Gogh": "model_painting.pth",
76
- "Cyberpunk": "model_cyberpunk.pth"
77
- }
78
  detail_prompt = {
79
  "Sketch": "charcoal sketch",
80
  "Van Gogh": "Van Gogh painting",
81
  "Cyberpunk": "neon cyberpunk",
82
  }
 
 
 
 
 
83
  '''
84
  try:
85
  style_net.load_state_dict(torch.load(weights_map.get(style_choice), map_location=DEVICE))
@@ -93,7 +93,7 @@ def edit_image(image, style_choice, num_steps):
93
  output_img = transforms.ToPILImage()(output_tensor.squeeze().cpu())
94
  return output_img
95
  '''
96
- return style_transfer(image, detail_prompt[style_choice], num_steps=num_steps)
97
 
98
  # --- THE UI ---
99
  with gr.Blocks() as demo:
@@ -115,18 +115,11 @@ with gr.Blocks() as demo:
115
  with gr.Row():
116
  im_in = gr.Image(type="pil", label="Input Image")
117
  style = gr.Dropdown(["Sketch", "Van Gogh", "Cyberpunk"], label="Choose Style")
118
- steps_slider = gr.Slider(
119
- minimum=50,
120
- maximum=200,
121
- value=100,
122
- step=10,
123
- label="Number of Steps (more = better quality but slower)"
124
- )
125
 
126
  btn_edit = gr.Button("Apply")
127
  im_out = gr.Image(label="Result")
128
 
129
- btn_edit.click(edit_image, inputs=[im_in, style, steps_slider], outputs=im_out)
130
 
131
  if __name__ == "__main__":
132
  demo.launch(share=True)
 
67
  transforms.ToTensor()
68
  ])
69
 
70
+ def edit_image(image, style_choice):
71
  if image is None: return None
72
 
 
 
 
 
 
73
  detail_prompt = {
74
  "Sketch": "charcoal sketch",
75
  "Van Gogh": "Van Gogh painting",
76
  "Cyberpunk": "neon cyberpunk",
77
  }
78
+ WEIGHTS_MAP = {
79
+ "Sketch": "unet_charcoal-sketch.pth",
80
+ "Van Gogh": "unet_van-gogh-painting.pth",
81
+ "Cyberpunk": "unet_neon-cyberpunk.pth"
82
+ }
83
  '''
84
  try:
85
  style_net.load_state_dict(torch.load(weights_map.get(style_choice), map_location=DEVICE))
 
93
  output_img = transforms.ToPILImage()(output_tensor.squeeze().cpu())
94
  return output_img
95
  '''
96
+ return style_transfer(image, detail_prompt[style_choice], weights_path=WEIGHTS_MAP[style_choice])
97
 
98
  # --- THE UI ---
99
  with gr.Blocks() as demo:
 
115
  with gr.Row():
116
  im_in = gr.Image(type="pil", label="Input Image")
117
  style = gr.Dropdown(["Sketch", "Van Gogh", "Cyberpunk"], label="Choose Style")
 
 
 
 
 
 
 
118
 
119
  btn_edit = gr.Button("Apply")
120
  im_out = gr.Image(label="Result")
121
 
122
+ btn_edit.click(edit_image, inputs=[im_in, style], outputs=im_out)
123
 
124
  if __name__ == "__main__":
125
  demo.launch(share=True)
clip_styler.py CHANGED
@@ -5,29 +5,18 @@ from util import *
5
  from cnn1 import UNet
6
  from transformers import CLIPModel, AutoTokenizer
7
  from torchvision.transforms.functional import adjust_contrast
 
8
 
9
- clip_model = None
10
- tokenizer = None
11
- vgg19 = None
12
- cnn_model = None
13
-
14
- def load_models():
15
- """Load models once and cache them"""
16
- global clip_model, tokenizer, vgg19, cnn_model
17
-
18
- if clip_model is None:
19
- clip_model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32").to(DEVICE)
20
-
21
- if tokenizer is None:
22
- tokenizer = AutoTokenizer.from_pretrained("openai/clip-vit-base-patch32")
23
 
24
- if vgg19 is None:
25
- vgg19 = models.vgg19(pretrained=True).features.to(DEVICE)
26
- for param in vgg19.parameters():
27
- param.requires_grad_(False)
 
28
 
29
- if cnn_model is None:
30
- cnn_model = UNet(text_dim=512).to(DEVICE)
31
 
32
  return clip_model, tokenizer, vgg19, cnn_model
33
 
@@ -42,7 +31,7 @@ def reset_cnn_model():
42
  if layer.bias is not None:
43
  torch.nn.init.zeros_(layer.bias)
44
 
45
- def style_transfer(img, prompt, num_steps=NUM_EPOCHS, source="a Photo"):
46
  """
47
  Apply style transfer to an uploaded image
48
 
@@ -60,33 +49,9 @@ def style_transfer(img, prompt, num_steps=NUM_EPOCHS, source="a Photo"):
60
  img = load_image(img).to(DEVICE)
61
 
62
  # Load models
63
- clip_model, tokenizer, vgg19, cnn_model = load_models()
64
-
65
- # Reset CNN model for fresh start on each image
66
- reset_cnn_model()
67
-
68
- # image features
69
- features = get_features(normalize(img), vgg19)
70
-
71
- # cnn model
72
- #cnn_model = UNet().to(DEVICE)
73
 
74
- # load ADAM
75
- optimizer = optim.Adam(cnn_model.parameters(), lr=LR)
76
- scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=100, gamma=0.5)
77
-
78
- # data augmentation
79
- crop = transforms.Compose([transforms.RandomCrop(CROP_SIZE)])
80
- augment = transforms.Compose([transforms.RandomPerspective(fill=0, p=1, distortion_scale=0.5), transforms.Resize(RESIZE)])
81
-
82
- # initialize variables
83
- content_loss_epoch = []
84
- style_loss_epoch = []
85
- total_loss_epoch = []
86
- output_image = img
87
- mean_img = torch.mean(img, dim=(2, 3), keepdim=False).squeeze(0)
88
- mean_img = [mean_img[0].item(), mean_img[1].item(), mean_img[2].item()]
89
- target = img.clone().requires_grad_(True).to(DEVICE)
90
 
91
  with torch.no_grad():
92
  edited_text = prompt_ensemble(prompt)
@@ -94,65 +59,9 @@ def style_transfer(img, prompt, num_steps=NUM_EPOCHS, source="a Photo"):
94
  text_features = clip_model.get_text_features(**text_tokens)
95
  text_features = text_features.mean(axis=0, keepdim=True)
96
  text_features /= text_features.norm(dim=-1, keepdim=True)
97
-
98
- edited_source = prompt_ensemble(source)
99
- source_tokens = tokenizer(edited_source, padding=True, return_tensors="pt").to(DEVICE)
100
- source_features = clip_model.get_text_features(**source_tokens)
101
- source_features = source_features.mean(axis=0, keepdim=True)
102
- source_features /= source_features.norm(dim=-1, keepdim=True)
103
-
104
- img_features = clip_model.get_image_features(pixel_values=clip_normalize(img))
105
- img_features /= (img_features.clone().norm(dim=-1, keepdim=True))
106
-
107
- for epoch in range(num_steps + 1):
108
- scheduler.step()
109
- #target = cnn_model(img).requires_grad_(True).to(DEVICE)
110
  target = cnn_model(img, text_embedding=text_features).to(DEVICE)
111
-
112
- target_features = get_features(normalize(target), vgg19)
113
-
114
- content_loss = 0.0
115
- content_loss += torch.mean((target_features['conv4_2'] - features['conv4_2']) ** 2)
116
- content_loss += torch.mean((target_features['conv5_2'] - features['conv5_2']) ** 2)
117
-
118
- loss_patch = 0
119
- img_proc = []
120
- for n in range(NUM_CROPS):
121
- target_crop = crop(target)
122
- target_crop = augment(target_crop)
123
- img_proc.append(target_crop)
124
-
125
- img_proc = torch.cat(img_proc, dim=0)
126
- img_aug = img_proc
127
-
128
- img_aug_features = clip_model.get_image_features(pixel_values=clip_normalize(img_aug))
129
- img_aug_features /= (img_aug_features.clone().norm(dim=-1, keepdim=True))
130
-
131
- img_direction = img_aug_features - img_features
132
- img_direction /= img_direction.clone().norm(dim=-1, keepdim=True)
133
-
134
- text_direction = (text_features - source_features).repeat(img_aug_features.size(0), 1)
135
- text_direction /= text_direction.norm(dim=-1, keepdim=True)
136
-
137
- loss_calc = (1 - torch.cosine_similarity(img_direction, text_direction, dim=1))
138
- loss_calc[loss_calc < PATCH_THRESHOLD] = 0.0
139
- loss_patch += loss_calc.mean()
140
-
141
- glob_features = clip_model.get_image_features(pixel_values=clip_normalize(target))
142
- glob_features /= (glob_features.clone().norm(dim=-1, keepdim=True))
143
- glob_direction = glob_features - img_features
144
- glob_direction /= glob_direction.clone().norm(dim=-1, keepdim=True)
145
-
146
- loss_glob = (1 - torch.cosine_similarity(glob_direction, text_direction, dim=1)).mean()
147
-
148
- loss_tv = L_TV * get_image_prior_losses(target)
149
-
150
- total_loss = (L_PATCH * loss_patch) + (L_DIR * loss_glob) + (L_CONTENT * content_loss) + loss_tv
151
- total_loss_epoch.append(total_loss)
152
-
153
- optimizer.zero_grad()
154
- total_loss.backward()
155
- optimizer.step()
156
 
157
  # Post-process output
158
  output_image = target.clone().detach()
 
5
  from cnn1 import UNet
6
  from transformers import CLIPModel, AutoTokenizer
7
  from torchvision.transforms.functional import adjust_contrast
8
+ import torch.nn.functional as F
9
 
10
+ def load_models(weights_path):
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ clip_model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32").to(DEVICE)
13
+ tokenizer = AutoTokenizer.from_pretrained("openai/clip-vit-base-patch32")
14
+ vgg19 = models.vgg19(pretrained=True).features.to(DEVICE)
15
+ for param in vgg19.parameters():
16
+ param.requires_grad_(False)
17
 
18
+ cnn_model = UNet(text_dim=512).to(DEVICE)
19
+ cnn_model.load_state_dict(torch.load(weights_path, map_location=DEVICE))
20
 
21
  return clip_model, tokenizer, vgg19, cnn_model
22
 
 
31
  if layer.bias is not None:
32
  torch.nn.init.zeros_(layer.bias)
33
 
34
+ def style_transfer(img, prompt, weights_path, source="a Photo"):
35
  """
36
  Apply style transfer to an uploaded image
37
 
 
49
  img = load_image(img).to(DEVICE)
50
 
51
  # Load models
52
+ clip_model, tokenizer, vgg19, cnn_model = load_models(weights_path)
 
 
 
 
 
 
 
 
 
53
 
54
+ cnn_model.eval()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  with torch.no_grad():
57
  edited_text = prompt_ensemble(prompt)
 
59
  text_features = clip_model.get_text_features(**text_tokens)
60
  text_features = text_features.mean(axis=0, keepdim=True)
61
  text_features /= text_features.norm(dim=-1, keepdim=True)
62
+
63
+ with torch.inference_mode():
 
 
 
 
 
 
 
 
 
 
 
64
  target = cnn_model(img, text_embedding=text_features).to(DEVICE)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
  # Post-process output
67
  output_image = target.clone().detach()