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