Deadmon commited on
Commit
91e8f79
·
verified ·
1 Parent(s): 35d7dfc

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +161 -0
app.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+ import gradio as gr
4
+ import numpy as np
5
+ from PIL import Image
6
+ from einops import rearrange
7
+ import requests
8
+ import spaces
9
+ from huggingface_hub import login
10
+ from gradio_imageslider import ImageSlider # Import ImageSlider
11
+
12
+ from image_datasets.canny_dataset import canny_processor, c_crop
13
+ from src.flux.sampling import denoise_controlnet, get_noise, get_schedule, prepare, unpack
14
+ from src.flux.util import load_ae, load_clip, load_t5, load_flow_model, load_controlnet, load_safetensors
15
+
16
+ # Define the ModelSamplingFlux class
17
+ class ModelSamplingFlux(torch.nn.Module):
18
+ def __init__(self, model_config=None):
19
+ super().__init__()
20
+ if model_config is not None:
21
+ sampling_settings = model_config.get("sampling_settings", {})
22
+ else:
23
+ sampling_settings = {}
24
+
25
+ self.set_parameters(shift=sampling_settings.get("shift", 1.15))
26
+
27
+ def set_parameters(self, shift=1.15, timesteps=10000):
28
+ self.shift = shift
29
+ ts = self.sigma((torch.arange(1, timesteps + 1, 1) / timesteps))
30
+ self.register_buffer('sigmas', ts)
31
+
32
+ @property
33
+ def sigma_min(self):
34
+ return self.sigmas[0]
35
+
36
+ @property
37
+ def sigma_max(self):
38
+ return self.sigmas[-1]
39
+
40
+ def timestep(self, sigma):
41
+ return sigma
42
+
43
+ def sigma(self, timestep):
44
+ return flux_time_shift(self.shift, 1.0, timestep)
45
+
46
+ def percent_to_sigma(self, percent):
47
+ if percent <= 0.0:
48
+ return 1.0
49
+ if percent >= 1.0:
50
+ return 0.0
51
+ return 1.0 - percent
52
+
53
+ # Download and load the ControlNet model
54
+ model_url = "https://huggingface.co/XLabs-AI/flux-controlnet-canny-v3/resolve/main/flux-canny-controlnet-v3.safetensors?download=true"
55
+ model_path = "./flux-canny-controlnet-v3.safetensors"
56
+ if not os.path.exists(model_path):
57
+ response = requests.get(model_url)
58
+ with open(model_path, 'wb') as f:
59
+ f.write(response.content)
60
+
61
+ # Source: https://github.com/XLabs-AI/x-flux.git
62
+ name = "flux-dev"
63
+ device = torch.device("cuda")
64
+ offload = False
65
+ is_schnell = name == "flux-schnell"
66
+
67
+ def preprocess_image(image, target_width, target_height, crop=True):
68
+ if crop:
69
+ image = c_crop(image) # Crop the image to square
70
+ original_width, original_height = image.size
71
+
72
+ # Resize to match the target size without stretching
73
+ scale = max(target_width / original_width, target_height / original_height)
74
+ resized_width = int(scale * original_width)
75
+ resized_height = int(scale * original_height)
76
+
77
+ image = image.resize((resized_width, resized_height), Image.LANCZOS)
78
+
79
+ # Center crop to match the target dimensions
80
+ left = (resized_width - target_width) // 2
81
+ top = (resized_height - target_height) // 2
82
+ image = image.crop((left, top, left + target_width, top + target_height))
83
+ return image
84
+
85
+ def preprocess_canny_image(image, target_width, target_height, crop=True):
86
+ image = preprocess_image(image, target_width, target_height, crop=crop)
87
+ image = canny_processor(image)
88
+ return image
89
+
90
+ @spaces.GPU(duration=120)
91
+ def generate_image(prompt, control_image, num_steps=50, guidance=4, width=512, height=512, seed=42, random_seed=False, max_shift=1.5, base_shift=1.15):
92
+ if random_seed:
93
+ seed = np.random.randint(0, 10000000)
94
+
95
+ if not os.path.isdir("./controlnet_results/"):
96
+ os.makedirs("./controlnet_results/")
97
+
98
+ torch_device = torch.device("cuda")
99
+
100
+ torch.cuda.empty_cache() # Clear GPU cache
101
+
102
+ model = load_flow_model(name, device=torch_device)
103
+ t5 = load_t5(torch_device, max_length=256 if is_schnell else 512)
104
+ clip = load_clip(torch_device)
105
+ ae = load_ae(name, device=torch_device)
106
+ controlnet = load_controlnet(name, torch_device).to(torch_device).to(torch.bfloat16)
107
+
108
+ checkpoint = load_safetensors(model_path)
109
+ controlnet.load_state_dict(checkpoint, strict=False)
110
+
111
+ width = 16 * width // 16
112
+ height = 16 * height // 16
113
+
114
+ # Initialize ModelSamplingFlux with the provided shifts
115
+ sampling_model = ModelSamplingFlux()
116
+ sampling_model.set_parameters(shift=base_shift, timesteps=num_steps)
117
+
118
+ timesteps = get_schedule(num_steps, (width // 8) * (height // 8) // (16 * 16), shift=max_shift)
119
+
120
+ processed_input = preprocess_image(control_image, width, height)
121
+ canny_processed = preprocess_canny_image(control_image, width, height)
122
+ controlnet_cond = torch.from_numpy((np.array(canny_processed) / 127.5) - 1)
123
+ controlnet_cond = controlnet_cond.permute(2, 0, 1).unsqueeze(0).to(torch.bfloat16).to(torch_device)
124
+
125
+ torch.manual_seed(seed)
126
+ with torch.no_grad():
127
+ x = get_noise(1, height, width, device=torch_device, dtype=torch.bfloat16, seed=seed)
128
+ inp_cond = prepare(t5=t5, clip=clip, img=x, prompt=prompt)
129
+
130
+ x = denoise_controlnet(model, **inp_cond, controlnet=controlnet, timesteps=timesteps, guidance=guidance, controlnet_cond=controlnet_cond)
131
+
132
+ x = unpack(x.float(), height, width)
133
+ x = ae.decode(x)
134
+
135
+ x1 = x.clamp(-1, 1)
136
+ x1 = rearrange(x1[-1], "c h w -> h w c")
137
+ output_img = Image.fromarray((127.5 * (x1 + 1.0)).cpu().byte().numpy())
138
+
139
+ return [processed_input, output_img] # Return both images for slider
140
+
141
+ interface = gr.Interface(
142
+ fn=generate_image,
143
+ inputs=[
144
+ gr.Textbox(label="Prompt"),
145
+ gr.Image(type="pil", label="Control Image"),
146
+ gr.Slider(step=1, minimum=1, maximum=64, value=28, label="Num Steps"),
147
+ gr.Slider(minimum=0.1, maximum=10, value=4, label="Guidance"),
148
+ gr.Slider(minimum=128, maximum=1024, step=128, value=512, label="Width"),
149
+ gr.Slider(minimum=128, maximum=1024, step=128, value=512, label="Height"),
150
+ gr.Slider(minimum=0, maximum=9999999, step=1, value=42, label="Seed"),
151
+ gr.Checkbox(label="Random Seed"),
152
+ gr.Slider(minimum=1.0, maximum=2.0, step=0.01, value=1.5, label="Max Shift"),
153
+ gr.Slider(minimum=1.0, maximum=2.0, step=0.01, value=1.15, label="Base Shift")
154
+ ],
155
+ outputs=ImageSlider(label="Before / After"), # Use ImageSlider as the output
156
+ title="FLUX.1 Controlnet Canny",
157
+ description="Generate images using ControlNet and a text prompt.\n[[non-commercial license, Flux.1 Dev](https://huggingface.co/black-forest-labs/FLUX.1-dev/blob/main/LICENSE.md)]"
158
+ )
159
+
160
+ if __name__ == "__main__":
161
+ interface.launch()