Spaces:
Sleeping
Sleeping
using inference
#3
by randomoneguy - opened
- app.py +8 -15
- 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
|
| 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],
|
| 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
|
| 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 |
-
|
| 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 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
| 28 |
|
| 29 |
-
|
| 30 |
-
|
| 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,
|
| 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 |
-
|
| 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 |
-
|
| 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()
|