File size: 10,498 Bytes
8f2ab11 1b06caa 7931060 8f2ab11 72f2543 ae2c112 8f2ab11 1b06caa 7931060 72f2543 7931060 8f2ab11 7931060 72f2543 7931060 1b06caa 7931060 e7d12ee 1b06caa 8f2ab11 1b06caa aaf6a9f 8f2ab11 aaf6a9f 8f2ab11 1b06caa aaf6a9f 8f2ab11 aaf6a9f 1b06caa aaf6a9f 1b06caa aaf6a9f 8f2ab11 aaf6a9f 1b06caa aaf6a9f 1b06caa 8f2ab11 1b06caa e7d12ee 8f2ab11 e7d12ee b5409e4 8f2ab11 e7d12ee 1b06caa 72f2543 e7d12ee 72f2543 e7d12ee 8f2ab11 e7d12ee ae2c112 8f2ab11 e7d12ee 8f2ab11 e7d12ee 1b06caa 72f2543 e7d12ee 72f2543 e7d12ee 72f2543 e7d12ee 72f2543 e7d12ee 36a3e6b 1b06caa 72f2543 1b06caa 72f2543 7931060 e7d12ee 8f2ab11 e7d12ee 8f2ab11 e7d12ee 72f2543 e7d12ee 1b06caa 72f2543 7931060 1b06caa 7931060 72f2543 1b06caa 7931060 8f2ab11 7931060 8f2ab11 7931060 8f2ab11 7931060 8f2ab11 |
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 |
# -*- coding: utf-8 -*-
import gradio as gr
# import requests # Nadal nieużywany bezpośrednio
from PIL import Image, ImageOps
import io
import os
import traceback
from gradio_client import Client, handle_file
import uuid
import shutil
from gradio_client.exceptions import AppError
print("--- Plik app.py - Start ładowania ---")
# --- Konfiguracja ---
API_TOKEN = os.getenv("HUGGINGFACE_API_TOKEN")
if not API_TOKEN:
print("!!! OSTRZEŻENIE: Nie znaleziono sekretu HUGGINGFACE_API_TOKEN. Klient Gradio spróbuje połączyć się anonimowo (mogą obowiązywać limity). !!!")
else:
print("--- Sekret HUGGINGFACE_API_TOKEN załadowany. ---")
LINKEDIN_PROMPT = (
"linkedin professional profile photo, corporate headshot, high quality, realistic photograph, "
"person wearing a dark business suit or elegant blouse, plain white background, "
"soft studio lighting, sharp focus, looking at camera, slight smile, natural skin texture"
)
TARGET_SPACE_ID = "InstantX/InstantID"
print(f"--- Konfiguracja załadowana. Cel dla gradio_client: {TARGET_SPACE_ID} ---")
# --- Logika aplikacji ---
def generate_photo(input_selfie, current_prompt):
print("\n--- Funkcja generate_photo (gradio_client) została wywołana ---")
# 1. Walidacja
if input_selfie is None:
print("BŁĄD: Nie wgrano zdjęcia wejściowego.")
raise gr.Error("Proszę najpierw wgrać swoje selfie!")
if not current_prompt:
print("BŁĄD: Prompt jest pusty.")
raise gr.Error("Prompt (opis zdjęcia) nie może być pusty!")
if not API_TOKEN:
print("INFO: Brak API Tokena. Połączenie z publicznym Space jako anonimowy użytkownik.")
print(f"Otrzymano obrazek typu: {type(input_selfie)}, Oryginalny rozmiar: {input_selfie.size}")
print(f"Otrzymano prompt (początek): {current_prompt[:100]}...")
# 2. Skalowanie obrazka
input_selfie_resized = None
try:
max_size = (1024, 1024)
input_selfie_resized = input_selfie.copy()
input_selfie_resized.thumbnail(max_size, Image.Resampling.LANCZOS)
print(f"Obrazek przeskalowany do (maksymalnie) {max_size}. Nowy rozmiar: {input_selfie_resized.size}")
except Exception as e_resize:
print(f"BŁĄD podczas skalowania obrazka: {e_resize}")
traceback.print_exc()
raise gr.Error(f"Nie można przeskalować obrazka wejściowego: {e_resize}")
# 3. Przygotowanie pliku tymczasowego
temp_dir = f"temp_input_{uuid.uuid4()}"
input_image_path = None
try:
os.makedirs(temp_dir, exist_ok=True)
input_image_path = os.path.join(temp_dir, "input_selfie.jpg")
rgb_image = input_selfie_resized
if rgb_image.mode == 'RGBA':
print("Konwertuję przeskalowany obraz RGBA na RGB z białym tłem...")
final_image = Image.new("RGB", rgb_image.size, (255, 255, 255))
final_image.paste(rgb_image, mask=rgb_image.split()[3])
elif rgb_image.mode != 'RGB':
print(f"Konwertuję przeskalowany obraz z trybu {rgb_image.mode} na RGB...")
final_image = rgb_image.convert('RGB')
else:
final_image = rgb_image
final_image.save(input_image_path, format="JPEG")
print(f"Przeskalowany obrazek wejściowy zapisany tymczasowo w: {input_image_path}")
except Exception as e_save:
print(f"BŁĄD podczas zapisywania obrazu tymczasowego: {e_save}")
traceback.print_exc()
if os.path.exists(temp_dir):
try: shutil.rmtree(temp_dir)
except Exception as e_clean: print(f"OSTRZEŻENIE: Nie udało się usunąć {temp_dir}: {e_clean}")
raise gr.Error(f"Problem z przygotowaniem obrazu do wysłania: {e_save}")
# 4. Wywołanie zdalnego API Gradio
output_image = None
client = None
try:
print(f"Łączenie z docelowym Space Gradio: {TARGET_SPACE_ID}")
client = Client(TARGET_SPACE_ID, hf_token=API_TOKEN)
# Usunięto odwołanie do client.space_host, zastąpiono prostszym printem:
print(f"Połączono z {TARGET_SPACE_ID}. Klient zainicjalizowany.")
print("Próbuję wywołać funkcję na zdalnym Space...")
negative_prompt = "ugly, deformed, noisy, blurry, low contrast, text, signature, watermark, duplicate, multiple people, cartoon, drawing, illustration, sketch, bad anatomy, worst quality, low quality"
style_name = "Realistic"
cn_scale = 0.8
ip_scale = 0.8
api_endpoint_name = "/generate_image"
print(f"Wywołuję endpoint '{api_endpoint_name}' z parametrami:")
print(f" Input image path: {input_image_path}")
print(f" Prompt (start): {current_prompt[:60]}...")
print(f" Negative Prompt (start): {negative_prompt[:60]}...")
print(f" Style: {style_name}")
print(f" ControlNet Scale: {cn_scale}")
print(f" IP-Adapter Scale: {ip_scale}")
# UWAGA: file() jest przestarzałe. Jeśli pojawią się błędy, spróbuj Client(..., serialize=False)
# i przekazuj pełną ścieżkę jako string: input_image_path zamiast file(input_image_path)
# Ale na razie zostawiamy file()
result = client.predict(
handle_file(input_image_path),
None,
current_prompt,
negative_prompt,
style_name,
cn_scale,
ip_scale,
api_name=api_endpoint_name
)
print(f"Otrzymano wynik od klienta: {type(result)}")
print(f"Wynik (fragment): {str(result)[:500]}")
if isinstance(result, list) and len(result) > 0 and isinstance(result[0], str) and os.path.exists(result[0]):
output_file_path = result[0]
print(f"Przetwarzam pierwszy obrazek wynikowy ze ścieżki: {output_file_path}")
output_image = Image.open(output_file_path)
print(f"Obrazek wynikowy załadowany pomyślnie. Rozmiar: {output_image.size}")
elif isinstance(result, str) and os.path.exists(result):
output_file_path = result
print(f"Przetwarzam obrazek wynikowy ze ścieżki: {output_file_path}")
output_image = Image.open(output_file_path)
print(f"Obrazek wynikowy załadowany pomyślnie. Rozmiar: {output_image.size}")
else:
print(f"BŁĄD: Otrzymano nieoczekiwany format wyniku od gradio_client: {type(result)}")
raise gr.Error(f"Nie udało się przetworzyć wyniku ze zdalnego API. Otrzymano: {str(result)[:200]}")
except AppError as e:
print(f"BŁĄD APLIKACJI ZDALNEJ (AppError): {e}")
traceback.print_exc()
error_message = f"Zdalny serwis ({TARGET_SPACE_ID}) zgłosił wewnętrzny błąd. Może to być spowodowane problemami z obrazkiem (np. brak wykrytej twarzy, nietypowy format), niedostępnością modelu lub przeciążeniem serwisu. Spróbuj z innym, wyraźnym zdjęciem twarzy lub spróbuj ponownie później."
raise gr.Error(error_message)
except ValueError as e:
print(f"BŁĄD WARTOŚCI (ValueError): {e}")
traceback.print_exc()
error_message = f"Wystąpił błąd wartości: {e}. "
if "Could not fetch config" in str(e):
error_message = f"Nie można pobrać konfiguracji zdalnego serwisu ({TARGET_SPACE_ID}). Może być chwilowo niedostępny, niekompatybilny lub wymagać logowania. Sprawdź status serwisu lub spróbuj później."
raise gr.Error(error_message)
except Exception as e:
print(f"NIEOCZEKIWANY BŁĄD: {e}")
traceback.print_exc()
error_message = f"Wystąpił nieoczekiwany błąd: {e}. Sprawdź logi aplikacji."
if "timed out" in str(e).lower():
error_message = "Przekroczono limit czasu oczekiwania na odpowiedź ze zdalnego serwisu. Może być przeciążony. Spróbuj ponownie."
elif "queue full" in str(e).lower():
error_message = "Kolejka w zdalnym serwisie jest pełna. Spróbuj ponownie za chwilę."
raise gr.Error(error_message)
finally:
# 5. Sprzątanie
if input_image_path and os.path.exists(temp_dir):
try:
shutil.rmtree(temp_dir)
print(f"Folder tymczasowy {temp_dir} usunięty.")
except Exception as e_clean:
print(f"OSTRZEŻENIE: Nie udało się usunąć folderu tymczasowego {temp_dir}: {e_clean}")
# 6. Zwróć wynik
if output_image:
print("Zwracam wygenerowany obrazek do interfejsu Gradio.")
return output_image
else:
print("BŁĄD KRYTYCZNY: Brak obrazka wynikowego, a nie zgłoszono błędu.")
raise gr.Error("Nie udało się uzyskać obrazka wynikowego z nieznanego powodu.")
# --- Budowa Interfejsu Gradio ---
print("--- Definiowanie interfejsu Gradio ---")
with gr.Blocks(css="footer {display: none !important;}", title="Generator Zdjęć Biznesowych") as demo:
gr.Markdown(
"""
# Generator Profesjonalnych Zdjęć Profilowych
Wgraj swoje selfie, a my (korzystając z modelu InstantID) postaramy się stworzyć profesjonalne zdjęcie w stylu LinkedIn!
**Wskazówki:**
* Użyj wyraźnego zdjęcia twarzy, patrzącej w miarę prosto, dobrze oświetlonej.
* Unikaj zdjęć grupowych, bardzo małych lub z mocno zasłoniętą twarzą.
* Generowanie może potrwać **od 30 sekund do kilku minut** (zwłaszcza za pierwszym razem). Bądź cierpliwy!
"""
)
with gr.Row():
with gr.Column(scale=1):
input_image = gr.Image(label="1. Wgraj swoje selfie (JPG/PNG)", type="pil", sources=["upload", "webcam"])
prompt_input = gr.Textbox(label="2. Opis pożądanego zdjęcia (prompt)", value=LINKEDIN_PROMPT, lines=4)
generate_button = gr.Button("✨ Generuj Zdjęcie Biznesowe ✨", variant="primary", scale=1)
with gr.Column(scale=1):
output_image = gr.Image(label="Oto Twoje wygenerowane zdjęcie:", type="pil")
generate_button.click(
fn=generate_photo,
inputs=[input_image, prompt_input],
outputs=[output_image]
)
print("--- Interfejs Gradio zdefiniowany ---")
# --- Uruchomienie aplikacji ---
if __name__ == "__main__":
print("--- Uruchamianie demo.launch() ---")
demo.launch()
print("--- Aplikacja Gradio zakończyła działanie ---") |