Superigni commited on
Commit
1762088
·
verified ·
1 Parent(s): 632f5c5

Flux Fusion [4 steps]

Browse files
Files changed (1) hide show
  1. app.py +165 -67
app.py CHANGED
@@ -1,86 +1,192 @@
1
  # app.py
2
  import gradio as gr
3
  import torch
4
- import numpy as np
5
  from PIL import Image
6
- import os # Хотя скачивание не используется, оставим на всякий случай
7
- # from tqdm import tqdm # Не используется в этом скрипте
 
8
 
9
  # Импорты для FLUX ControlNet пайплайна
 
10
  from diffusers import FluxControlNetPipeline, ControlNetModel, FluxPipeline
11
  # from diffusers.utils import load_image # Не нужен для этого кода
12
 
13
- # --- Определение ID моделей FLUX на Hugging Face Hub ---
14
- # Базовая модель FLUX (ОГРАНИЧЕННЫЙ ДОСТУП - требуется токен HF и доступ к репо)
15
- BASE_FLUX_MODEL_ID = "black-forest-labs/FLUX.1-dev"
16
- # ControlNet модель для FLUX (также на HF Hub)
17
- CONTROLNET_FLUX_MODEL_ID = "ABDALLALSWAITI/FLUX.1-dev-ControlNet-Union-Pro-2.0-fp8"
 
 
 
 
 
 
18
 
 
 
19
 
20
- # Переменная для хранения пайплайна (будет загружен при запуске скрипта)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  pipeline = None
 
 
 
 
 
22
 
23
- # --- Загрузка пайплайна FLUX ControlNet ---
24
  # Эта функция вызывается один раз при запуске скрипта
25
- def load_flux_pipeline(base_model_id, controlnet_model_id):
26
- """Загружает пайплайн FLUX ControlNet с Hugging Face Hub."""
27
- print(f"Начинаю загрузку пайплайна FLUX ControlNet...")
28
- print(f"Базовая модель FLUX: {base_model_id}")
29
- print(f"ControlNet модель FLUX: {controlnet_model_id}")
 
 
 
30
 
 
31
  try:
32
- # Пайплайн FluxControlNetPipeline загружает и объединяет обе модели из репозиториев HF
33
- # from_pretrained автоматически использует HF_TOKEN, если он установлен как секрет в Space
34
- # Убедитесь, что версия diffusers поддерживает этот пайплайн и модели FLUX
35
- pipe = FluxControlNetPipeline.from_pretrained(
36
- base_model_id,
37
- controlnet=controlnet_model_id, # Передаем ID ControlNet модели
38
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
39
- # safety_checker=None # Обычно from_pretrained для FLUX пайплайна не принимает этот аргумент напрямую
40
- )
41
 
42
- # Для FLUX планировщик специфичный, from_pretrained должен загрузить правильный.
43
- print(f"Планировщик загружен: {type(pipe.scheduler).__name__}")
 
44
 
45
- # Перемещаем пайплайн на GPU, если доступно
46
- if torch.cuda.is_available():
47
- pipe = pipe.to("cuda")
48
- print("Пайплайн FLUX ControlNet перемещен на GPU.")
49
- else:
50
- print("GPU не найдено. Пайплайн будет работать на CPU (не рекомендуется для FLUX).")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- print("Загрузка пайплайна FLUX ControlNet завершена успешно.")
53
- return pipe # Возвращаем готовый пайплайн
54
 
55
  except Exception as e:
56
- print(f"Ошибка при загрузке пайплайна FLUX ControlNet с Hugging Face Hub: {e}")
57
- print(f"Частые причины:")
58
- print(f"- Ваш аккаунт не имеет доступа к '{base_model_id}' (нужно зайти на страницу модели на hf.co и принять условия).")
59
- print(f"- Секрет HF_TOKEN неправильно установлен в настройках Space или не имеет достаточных прав.")
60
- print(f"- Указан неверный ID модели.")
61
- print(f"- Проблемы с интернет-соединением Space.")
62
- print(f"- Версия библиотеки diffusers слишком старая для моделей FLUX.")
63
- print(f"Подробности ошибки: {e}")
64
- return None # Возвращаем None, если загрузка не удалась
65
 
66
 
67
  # --- Загружаем пайплайн при запуске скрипта ---
68
- # Этот код выполняется один раз при старте Space
69
- pipeline = load_flux_pipeline(BASE_FLUX_MODEL_ID, CONTROLNET_FLUX_MODEL_ID)
 
 
 
70
 
71
 
72
  # --- Функция рендеринга для Gradio ---
73
  # Эта функция будет вызываться интерфейсом Gradio в Space
74
- def generate_image_gradio(controlnet_image: np.ndarray, prompt: str, negative_prompt: str = "", guidance_scale: float = 7.0, num_inference_steps: int = 50, controlnet_conditioning_scale: float = 1.0):
 
75
  """
76
  Генерирует изображение с использованием FLUX ControlNet.
77
  Принимает изображение NumPy, текст промта и другие параметры.
78
  Возвращает сгенерированное изображение в формате PIL Image.
79
  """
80
- # Проверяем, успешно ли загрузился пайплайн
81
  if pipeline is None:
82
  print("Попытка генерации, но пайплайн модели не загружен.")
83
- return None, "Ошибка: Пайплайн модели FLUX не загружен. Проверьте логи Space и доступ к моделям."
84
 
85
  if controlnet_image is None:
86
  return None, "Ошибка: необходимо загрузить изображение для ControlNet."
@@ -88,16 +194,12 @@ def generate_image_gradio(controlnet_image: np.ndarray, prompt: str, negative_pr
88
  print(f"Генерация изображения FLUX с промтом: '{prompt}'")
89
  print(f"Размер входного изображения для ControlNet: {controlnet_image.shape}")
90
 
91
- # Gradio возвращает изображение как numpy array. Преобразуем в PIL Image для пайплайна.
92
- # Пайплайны ControlNet обычно ожидают PIL Image в RGB.
93
  input_image_pil = Image.fromarray(controlnet_image).convert("RGB")
94
 
95
  # Выполняем рендеринг с помощью пайплайна FLUX ControlNet
96
- # Параметры для FLUX могут немного отличаться от SD, проверьте документацию diffusers для FluxControlNetPipeline
97
- # guidance_scale и num_inference_steps - стандартные параметры
98
- # controlnet_conditioning_scale - стандартный параметр ControlNet
99
  try:
100
  # Вызов пайплайна FLUX ControlNet
 
101
  output = pipeline(
102
  prompt=prompt,
103
  image=input_image_pil, # Входное изображение для ControlNet
@@ -105,29 +207,28 @@ def generate_image_gradio(controlnet_image: np.ndarray, prompt: str, negative_pr
105
  guidance_scale=guidance_scale,
106
  num_inference_steps=num_inference_steps,
107
  controlnet_conditioning_scale=controlnet_conditioning_scale,
108
- # Другие параметры, специфичные для FLUX, могут быть доступны здесь.
109
- # Проверьте сигнатуру вызова пайплайна FLUX в diffusers.
110
  )
111
 
112
- # Результат находится в output.images[0]
113
  generated_image_pil = output.images[0]
114
 
115
  print("Генерация FLUX завершена.")
116
  return generated_image_pil, "Успех!"
117
  except Exception as e:
118
  print(f"Ошибка при генерации FLUX: {e}")
119
- # Возвращаем None и сообщение об ошибке в интерфейс Gradio
120
  return None, f"Ошибка при генерации FLUX: {e}"
121
 
122
 
123
  # --- Настройка интерфейса Gradio ---
124
- # Определяем входные и выходные элементы
125
- # Элементы интерфейса могут остаться теми же, так как они универсальны
126
  input_image_comp = gr.Image(type="numpy", label="Изображение для ControlNet (набросок, карта глубины и т.д.)")
127
  prompt_comp = gr.Textbox(label="Промт (Prompt)")
128
  negative_prompt_comp = gr.Textbox(label="Негативный промт (Negative Prompt)")
129
- guidance_scale_comp = gr.Slider(minimum=1.0, maximum=20.0, value=7.0, step=0.1, label="Степень соответствия промту (Guidance Scale)")
130
- num_inference_steps_comp = gr.Slider(minimum=10, maximum=150, value=50, step=1, label="Количество шагов (Inference Steps)") # Шаги для FLUX могут отличаться
 
 
131
  controlnet_conditioning_scale_comp = gr.Slider(minimum=0.0, maximum=2.0, value=1.0, step=0.05, label="Вес ControlNet (ControlNet Scale)")
132
 
133
  output_image_comp = gr.Image(type="pil", label="Сгенерированное изображение")
@@ -135,8 +236,6 @@ status_text_comp = gr.Textbox(label="Статус")
135
 
136
 
137
  # Создаем интерфейс Gradio
138
- # Поскольку мы в Space, Gradio SDK сам вызовет interface.launch()
139
- # Нам просто нужно определить объект интерфейса
140
  interface = gr.Interface(
141
  fn=generate_image_gradio,
142
  inputs=[
@@ -148,9 +247,8 @@ interface = gr.Interface(
148
  controlnet_conditioning_scale_comp
149
  ],
150
  outputs=[output_image_comp, status_text_comp],
151
- title="FLUX ControlNet Interface on HF Space",
152
- description="Загрузите изображение для ControlNet, введите промт и нажмите 'Generate'. Используются модели FLUX и FLUX ControlNet с Hugging Face Hub."
153
  )
154
 
155
- # Нет необходимости вызывать interface.launch() в блоке if __name__ == "__main__":
156
- # Gradio SDK в Space сделает это автоматически при запуске скрипта.
 
1
  # app.py
2
  import gradio as gr
3
  import torch
4
+ import requests
5
  from PIL import Image
6
+ import numpy as np
7
+ import os
8
+ from tqdm import tqdm
9
 
10
  # Импорты для FLUX ControlNet пайплайна
11
+ # Возможно, потребуются дополнительные импорты компонентов FLUX, если from_single_file не сработает
12
  from diffusers import FluxControlNetPipeline, ControlNetModel, FluxPipeline
13
  # from diffusers.utils import load_image # Не нужен для этого кода
14
 
15
+ # --- Вспомогательная функция для скачивания файлов ---
16
+ def download_file(url, local_filename):
17
+ """Скачивает файл по URL с индикатором прогресса."""
18
+ print(f"Скачиваю {url} в {local_filename}...")
19
+ if os.path.exists(local_filename):
20
+ print(f"Файл уже существует: {local_filename}. Пропускаю скачивание.")
21
+ return local_filename
22
+
23
+ try:
24
+ response = requests.get(url, stream=True)
25
+ response.raise_for_status()
26
 
27
+ total_size_in_bytes = int(response.headers.get('content-length', 0))
28
+ block_size = 8192
29
 
30
+ if total_size_in_bytes > 0:
31
+ progress_bar = tqdm(total=total_size_in_bytes, unit='iB', unit_scale=True, desc=f"Скачивание {local_filename}")
32
+ else:
33
+ print("Размер файла неизвестен, скачивание без индикатора прогресса.")
34
+ progress_bar = None
35
+
36
+ with open(local_filename, 'wb') as f:
37
+ for chunk in response.iter_content(chunk_size=block_size):
38
+ if progress_bar:
39
+ progress_bar.update(len(chunk))
40
+ f.write(chunk)
41
+
42
+ if progress_bar:
43
+ progress_bar.close()
44
+
45
+ print(f"Скачивание завершено: {local_filename}")
46
+ return local_filename
47
+ except requests.exceptions.RequestException as e:
48
+ print(f"Ошибка скачивания {url}: {e}")
49
+ return None
50
+ except Exception as e:
51
+ print(f"Произошла другая ошибка при скачивании: {e}")
52
+ return None
53
+
54
+ # --- Определение путей/ID моделей ---
55
+ # URL SafeTensor модели "Flux Fusion V2" с Civitai (FP8)
56
+ CIVITAI_FLUX_FUSION_URL = "https://civitai.com/api/download/models/936565?type=Model&format=SafeTensor&fp=fp8"
57
+ # Локальное имя файла для сохранения SafeTensor модели
58
+ LOCAL_FLUX_FUSION_FILENAME = "flux_fusion_v2_fp8.safetensors"
59
+
60
+ # ControlNet модель для FLUX с Hugging Face
61
+ CONTROLNET_FLUX_MODEL_ID = "ABDALLALSWAITI/FLUX.1-dev-ControlNet-Union-Pro-2.0-fp8"
62
+
63
+ # Переменная для хранения пайплайна
64
  pipeline = None
65
+ downloaded_base_model_path = None
66
+
67
+ # --- Скачиваем SafeTensor модель с Civitai ---
68
+ print("Начинаю скачивание базовой модели с Civitai...")
69
+ downloaded_base_model_path = download_file(CIVITAI_FLUX_FUSION_URL, LOCAL_FLUX_FUSION_FILENAME)
70
 
71
+ # --- Загрузка моделей и создание пайплайна ---
72
  # Эта функция вызывается один раз при запуске скрипта
73
+ def load_pipeline_components(base_model_path, controlnet_model_id):
74
+ """
75
+ Загружает ControlNet с HF и пытается собрать пайплайн FLUX,
76
+ используя локальный SafeTensor как базовую модель.
77
+ """
78
+ if not base_model_path or not os.path.exists(base_model_path):
79
+ print(f"Ошибка загрузки: Файл базовой модели не найден по пути: {base_model_path}")
80
+ return None
81
 
82
+ print(f"З��грузка ControlNet модели FLUX с HF Hub: {controlnet_model_id}")
83
  try:
84
+ # Загрузка ControlNet для FLUX
85
+ controlnet = ControlNetModel.from_pretrained(controlnet_model_id, torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32)
86
+ except Exception as e:
87
+ print(f"Ошибка загрузки ControlNet модели с HF Hub: {controlnet_model_id}. Проверьте ID или соединение.")
88
+ print(f"Ошибка: {e}")
89
+ return None
 
 
 
90
 
91
+ print(f"Попытка собрать пайплайн FLUX ControlNet, используя локальный файл: {base_model_path} как базовую модель.")
92
+ print("ВНИМАНИЕ: Загрузка FLUX пайплайна из одиночного SafeTensor файла методом from_single_file")
93
+ print("не является стандартной и может вызвать ошибки совместимости.")
94
 
95
+ try:
96
+ # !!! ЭТО САМАЯ ПРОБЛЕМНАЯ ЧАСТЬ !!!
97
+ # from_single_file разработан для SD. Попытка использовать его для FLUX SafeTensor может не сработать.
98
+ # from_pretrained для FluxControlNetPipeline ожидает ID репозитория HF или локальную ПАПКУ.
99
+ # Здесь мы пытаемся передать локальный *файл*. Это нестандартно.
100
+ # Возможно, придется явно указывать тип модели или компоненты, если from_single_file не сработает.
101
+ # Например: FluxPipeline.from_single_file() если такой метод есть и работает для FLUX.
102
+ # Или даже собрать вручную: FluxPipeline(transformer=..., vae=..., ...).from_single_file(...)
103
+ # Попробуем передать файл в from_pretrained, хотя он обычно ждет папку/ID.
104
+ # Или попытаемся использовать from_single_file, хотя он для SD.
105
+ # Основываясь на предыдущем опыте, from_single_file "пытается" понять структуру.
106
+ # Давайте попробуем from_single_file, но с большим сомнением в успехе для FLUX.
107
+
108
+ # Попытка 1: from_single_file (наиболее вероятный источник ошибок для FLUX SafeTensor)
109
+ # УКАЗЫВАЕМ ЯВНО controlnet=None при загрузке БАЗОВОГО пайплайна из файла
110
+ # ControlNetModel передадим позже при создании FluxControlNetPipeline
111
+ base_pipe = FluxPipeline.from_single_file(
112
+ base_model_path,
113
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
114
+ # Возможно, придется передавать явно другие компоненты, если они не в файле
115
+ # controlnet=None # from_single_file не принимает controlnet
116
+ )
117
+ print("Успешно загружен базовый FLUX пайплайн из SafeTensor файла методом from_single_file (если это сообщение видно).")
118
+
119
+
120
+ # --- Создание финального пайплайна FluxControlNetPipeline из компонентов ---
121
+ # Собираем пайплайн, используя компоненты из базового пайплайна и ControlNet
122
+ print("Собираю финальный FluxControlNetPipeline...")
123
+ # Нужно убедиться, что у base_pipe есть все необходимые для FLUX компоненты (transformer, vae, etc.)
124
+ # from_single_file мог загрузить только часть
125
+ try:
126
+ controlnet_pipe = FluxControlNetPipeline(
127
+ transformer=base_pipe.transformer, # Основной компонент FLUX
128
+ vae=base_pipe.vae,
129
+ text_encoder=base_pipe.text_encoder, # У FLUX есть text_encoder, но другой, не как у SD CLIP
130
+ tokenizer=base_pipe.tokenizer,
131
+ scheduler=base_pipe.scheduler,
132
+ controlnet=controlnet, # Передаем загруженный FLUX ControlNet
133
+ feature_extractor=base_pipe.feature_extractor if hasattr(base_pipe, 'feature_extractor') else None, # Копируем feature_extractor
134
+ image_processor=base_pipe.image_processor if hasattr(base_pipe, 'image_processor') else None, # Копируем image_processor
135
+ )
136
+
137
+ # Планировщик должен быть FLUX-совместимым, from_single_file или from_pretrained должны его загрузить.
138
+ print(f"Финальный планировщик: {type(controlnet_pipe.scheduler).__name__}")
139
+
140
+ # Удаляем старый объект пайплайна для освобождения памяти GPU
141
+ del base_pipe
142
+ if torch.cuda.is_available():
143
+ torch.cuda.empty_cache()
144
+ print("Память GPU очищена после создания ControlNet пайплайна.")
145
+
146
+ # Перемещаем пайплайн на GPU
147
+ if torch.cuda.is_available():
148
+ controlnet_pipe = controlnet_pipe.to("cuda")
149
+ print("Финальный пайплайн FLUX ControlNet перемещен на GPU.")
150
+ else:
151
+ print("GPU не найдено. Пайплайн на CPU.")
152
+
153
+ print("Сборка финального пайплайна FLUX ControlNet завершена успешно.")
154
+ return controlnet_pipe
155
+
156
+ except Exception as e:
157
+ print(f"Ошибка при сборке финального FluxControlNetPipeline: {e}")
158
+ print("Проверьте, что базовая модель, загруженная из SafeTensor, содержит все компоненты FLUX (transformer, vae, text_encoder и т.д.).")
159
+ print("Возможно, from_single_file не смог загрузить все необходимые компоненты FLUX из этого файла.")
160
+ return None
161
 
 
 
162
 
163
  except Exception as e:
164
+ print(f"Критическая ошибка при попытке загрузить базовый FLUX пайплайн из файла {base_model_path}: {e}")
165
+ print("Наиболее вероятно, этот файл SafeTensor несовместим с методами загрузки FLUX в diffusers.")
166
+ print("Возможно, файл поврежден или не содержит ожидаемой структуры FLUX.")
167
+ return None
 
 
 
 
 
168
 
169
 
170
  # --- Загружаем пайплайн при запуске скрипта ---
171
+ if downloaded_base_model_path and os.path.exists(downloaded_base_model_path):
172
+ pipeline = load_pipeline_components(downloaded_base_model_path, CONTROLNET_FLUX_MODEL_ID)
173
+ else:
174
+ print("Пропуск загру��ки пайплайна из-за ошибки скачивания или отсутствия файла.")
175
+ pipeline = None
176
 
177
 
178
  # --- Функция рендеринга для Gradio ---
179
  # Эта функция будет вызываться интерфейсом Gradio в Space
180
+ # Параметры могут потребовать настройки для конкретной модели FLUX Fusion
181
+ def generate_image_gradio(controlnet_image: np.ndarray, prompt: str, negative_prompt: str = "", guidance_scale: float = 5.0, num_inference_steps: int = 4, controlnet_conditioning_scale: float = 1.0): # Значения по умолчанию подстроены под Flux Fusion
182
  """
183
  Генерирует изображение с использованием FLUX ControlNet.
184
  Принимает изображение NumPy, текст промта и другие параметры.
185
  Возвращает сгенерированное изображение в формате PIL Image.
186
  """
 
187
  if pipeline is None:
188
  print("Попытка генерации, но пайплайн модели не загружен.")
189
+ return None, "Ошибка: Пайплайн модели не загружен. Проверьте логи Space."
190
 
191
  if controlnet_image is None:
192
  return None, "Ошибка: необходимо загрузить изображение для ControlNet."
 
194
  print(f"Генерация изображения FLUX с промтом: '{prompt}'")
195
  print(f"Размер входного изображения для ControlNet: {controlnet_image.shape}")
196
 
 
 
197
  input_image_pil = Image.fromarray(controlnet_image).convert("RGB")
198
 
199
  # Выполняем рендеринг с помощью пайплайна FLUX ControlNet
 
 
 
200
  try:
201
  # Вызов пайплайна FLUX ControlNet
202
+ # Проверьте документацию diffusers для FluxControlNetPipeline для точных параметров вызова
203
  output = pipeline(
204
  prompt=prompt,
205
  image=input_image_pil, # Входное изображение для ControlNet
 
207
  guidance_scale=guidance_scale,
208
  num_inference_steps=num_inference_steps,
209
  controlnet_conditioning_scale=controlnet_conditioning_scale,
210
+ # Для FLUX Fusion [4 steps], количество шагов (num_inference_steps) очень низкое!
211
+ # Возможно, нужно использовать фиксированное значение 4, несмотря на ползунок?
212
  )
213
 
 
214
  generated_image_pil = output.images[0]
215
 
216
  print("Генерация FLUX завершена.")
217
  return generated_image_pil, "Успех!"
218
  except Exception as e:
219
  print(f"Ошибка при генерации FLUX: {e}")
 
220
  return None, f"Ошибка при генерации FLUX: {e}"
221
 
222
 
223
  # --- Настройка интерфейса Gradio ---
224
+ # Параметры по умолчанию подстроены под Flux Fusion [4 steps]
 
225
  input_image_comp = gr.Image(type="numpy", label="Изображение для ControlNet (набросок, карта глубины и т.д.)")
226
  prompt_comp = gr.Textbox(label="Промт (Prompt)")
227
  negative_prompt_comp = gr.Textbox(label="Негативный промт (Negative Prompt)")
228
+ # Guidance Scale для FLUX Fusion может быть ниже, чем для SD
229
+ guidance_scale_comp = gr.Slider(minimum=0.0, maximum=10.0, value=5.0, step=0.1, label="Степень соответствия промту (Guidance Scale)")
230
+ # Количество шагов для FLUX Fusion [4 steps] ОЧЕНЬ низкое
231
+ num_inference_steps_comp = gr.Slider(minimum=1, maximum=20, value=4, step=1, label="Количество шагов (Inference Steps) [для FLUX Fusion V2 обычно 4]")
232
  controlnet_conditioning_scale_comp = gr.Slider(minimum=0.0, maximum=2.0, value=1.0, step=0.05, label="Вес ControlNet (ControlNet Scale)")
233
 
234
  output_image_comp = gr.Image(type="pil", label="Сгенерированное изображение")
 
236
 
237
 
238
  # Создаем интерфейс Gradio
 
 
239
  interface = gr.Interface(
240
  fn=generate_image_gradio,
241
  inputs=[
 
247
  controlnet_conditioning_scale_comp
248
  ],
249
  outputs=[output_image_comp, status_text_comp],
250
+ title="FLUX ControlNet Interface (Attempt with Civitai SafeTensor)",
251
+ description="Загрузите изображение для ControlNet, введите промт и нажмите 'Generate'. Попытка использовать SafeTensor 'Flux Fusion V2' с Civitai как базовую модель FLUX с ControlNet с HF."
252
  )
253
 
254
+ # Запуск в Space обрабатывается SDK.