import os import random import gradio as gr from gradio_client import Client, handle_file from PIL import Image from deep_translator import GoogleTranslator from langdetect import detect import requests def get_random_api_key(): keys = os.getenv("KEYS", "").split(",") if keys and keys[0]: # Check if KEYS is set and not empty return random.choice(keys).strip() else: raise ValueError("API keys not found. Please set the KEYS environment variable.") # Константа NSFW_THRESHOLD = 0.85 # Ссылка на файл CSS css_url = "https://neurixyufi-aihub.static.hf.space/style.css" # Получение CSS по ссылке try: response = requests.get(css_url) response.raise_for_status() # Поднимаем исключение, если статус ответа не 200 css = response.text + " h1{text-align:center}" except requests.exceptions.RequestException as e: print(f"Ошибка при загрузке CSS: {e}") css = " h1{text-align:center}" # Используем базовый стиль, если загрузка CSS не удалась def create_demo(args, model_name: str, device: str = "cpu", offload: bool = False): with gr.Blocks(css=css) as demo: def generate_image( prompt, id_image, start_step, guidance, seed, true_cfg, width=896, height=1152, num_steps=20, id_weight=1.0, neg_prompt="плохое качество, очень плохое качество, текст, подпись, водяной знак, лишние конечности", timestep_to_start_cfg=1, max_sequence_length=128, ): client = Client("yanze/PuLID-FLUX", hf_token=get_random_api_key()) if seed == -1: seed = None else: seed = int(seed) language = detect(prompt) if language != 'en': prompt = GoogleTranslator(source=language, target='en').translate(prompt) languagen = detect(neg_prompt) if languagen != 'en': neg_prompt = GoogleTranslator(source=languagen, target='en').translate(neg_prompt) try: if id_image is not None: # Сохраняем изображение во временный файл temp_file_path = "/tmp/id_image.png" Image.fromarray(id_image).save(temp_file_path) id_image_for_api = handle_file(temp_file_path) else: id_image_for_api = None result = client.predict( prompt, id_image_for_api, start_step, guidance, str(seed), # Преобразуем seed в строку true_cfg, width, height, num_steps, id_weight, neg_prompt, timestep_to_start_cfg, max_sequence_length, api_name="/generate_image" ) generated_image, used_seed, debug_images = result if generated_image is None: return None, used_seed, debug_images # Возвращаем сообщение об ошибке из API else: pil_image = Image.open(generated_image) return pil_image, used_seed, debug_images except Exception as e: return None, f"Ошибка при генерации: {e}", [] gr.Markdown("# Редактор фото") with gr.Row(): with gr.Column(): prompt = gr.Textbox(label="Описание", placeholder="портрет, цветное, кинематографическое") id_image = gr.Image(label="Изображение") id_weight = gr.Slider(0.0, 3.0, 1, step=0.05, label="Идентичность") width = gr.Slider(256, 1536, 896, step=16, label="Ширина") height = gr.Slider(256, 1536, 1152, step=16, label="Высота") num_steps = gr.Slider(1, 20, 20, step=1, label="Количество шагов обработки") start_step = gr.Slider(0, 10, 0, step=1, label="Шаг начала внедрения оригинала") guidance = gr.Slider(1.0, 10.0, 4, step=0.1, label="Совпадение с описанием") seed = gr.Textbox(-1, label="Сид (-1 для случайного)") max_sequence_length = gr.Slider(128, 512, 128, step=128, label="Максимальная длина последовательности для описания (T5), меньше будет быстрее") with gr.Accordion("Дополнительные опции", open=False): neg_prompt = gr.Textbox( label="Отрицательное описание", value="плохое качество, очень плохое качество, текст, подпись, водяной знак, лишние конечности") true_cfg = gr.Slider(1.0, 10.0, 1, step=0.1, label="Масштаб true CFG") timestep_to_start_cfg = gr.Slider(0, 20, 1, step=1, label="Шаг начала CFG", visible=args.dev) generate_btn = gr.Button("Изменить") with gr.Column(): output_image = gr.Image(label="Сгенерированное изображение", format='png') seed_output = gr.Textbox(label="Использованный сид") intermediate_output = gr.Gallery(label='Промежуточные изображения', elem_id="gallery", visible=args.dev) with gr.Row(), gr.Column(): gr.Markdown("## Примеры") example_inps = [ [ 'женщина с табличкой с зелёным текстом "Im Human"', 'https://huggingface.co/spaces/yanze/PuLID-FLUX/resolve/main/example_inputs/liuyifei.png', 4, 4, 2680261499100305976, 1 ], [ 'портрет, боковое видение', 'https://huggingface.co/spaces/yanze/PuLID-FLUX/resolve/main/example_inputs/liuyifei.png', 4, 4, 180825677246321775, 1 ], [ 'блондинка с технологией VR, революционный магнум с деталями', 'https://huggingface.co/spaces/yanze/PuLID-FLUX/resolve/main/example_inputs/liuyifei.png', 4, 4, 16942328329935464989, 1 ], [ 'малыш ест мороженое', 'https://huggingface.co/spaces/yanze/PuLID-FLUX/resolve/main/example_inputs/liuyifei.png', 4, 4, 4527590969012358757, 1 ], [ 'мужчина с табличкой с текстом "Im Human", зима, снегопад, вершина горы', 'https://huggingface.co/spaces/yanze/PuLID-FLUX/resolve/main/example_inputs/pengwei.jpg', 4, 4, 6273700647573240909, 1 ], [ 'портрет, свет свечей', 'https://huggingface.co/spaces/yanze/PuLID-FLUX/resolve/main/example_inputs/pengwei.jpg', 4, 4, 17522759474323955700, 1 ], [ 'кадр с тёмным фото 25-летнего мужчины с дымом изо рта, снимок в обратном освещении, естественное лицо, естественные брови, естественная текстура кожи, наградное фото, деталированное лицо, атмосферное освещение, зернистость, монохромное', 'https://huggingface.co/spaces/yanze/PuLID-FLUX/resolve/main/example_inputs/pengwei.jpg', 4, 4, 17733156847328193625, 1 ], [ 'американская комикса, мальчик', 'https://huggingface.co/spaces/yanze/PuLID-FLUX/resolve/main/example_inputs/pengwei.jpg', 1, 4, 13223174453874179686, 1 ], [ 'портрет, стилизация Pixar', 'https://huggingface.co/spaces/yanze/PuLID-FLUX/resolve/main/example_inputs/pengwei.jpg', 1, 4, 9445036702517583939, 1 ], ] gr.Examples(examples=example_inps, inputs=[prompt, id_image, start_step, guidance, seed, true_cfg], label='Использование fake CFG', cache_examples='lazy', outputs=[output_image, seed_output], fn=generate_image) example_inps = [ [ 'портрет, сделанный из льда', 'example_inputs/lecun.jpg', 1, 1, 7717391560531186077, 5 ], ] gr.Examples(examples=example_inps, inputs=[prompt, id_image, start_step, guidance, seed, true_cfg], label='Использование true CFG', cache_examples='lazy', outputs=[output_image, seed_output], fn=generate_image) generate_btn.click( fn=generate_image, inputs=[prompt, id_image, start_step, guidance, seed, true_cfg, width, height, num_steps, id_weight, neg_prompt, timestep_to_start_cfg, max_sequence_length], outputs=[output_image, seed_output, intermediate_output], ) return demo if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(description="PuLID для FLUX") parser.add_argument("--name", type=str, default="flux-dev", choices=["flux-dev"], help="в настоящее время поддерживается только flux-dev") parser.add_argument("--device", type=str, default="cpu", help="Устройство для использования") parser.add_argument("--offload", action="store_true", help="Выгрузка модели на CPU при неприменении") parser.add_argument("--port", type=int, default=8080, help="Порт для использования") parser.add_argument("--dev", action='store_true', help="Режим разработки") parser.add_argument("--pretrained_model", type=str, help='для разработки') args = parser.parse_args() demo = create_demo(args, args.name, args.device, args.offload) demo.launch(server_port=7860)