Update app.py
Browse files
app.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1 |
import gradio as gr
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
import
|
6 |
-
import
|
|
|
|
|
7 |
from gradio_client import Client, file
|
8 |
import uuid
|
9 |
import shutil
|
@@ -12,194 +14,236 @@ print("--- Plik app.py - Start ładowania ---")
|
|
12 |
|
13 |
# --- Konfiguracja ---
|
14 |
# Odczytanie sekretu (API Token) ustawionego w Space
|
15 |
-
# PAMIĘTAJ, ABY DODAĆ TEN SEKRET W USTAWIENIACH SPACE'A!
|
16 |
-
# Settings -> Repository secrets -> New secret
|
17 |
-
# Name: HUGGINGFACE_API_TOKEN
|
18 |
-
# Value: <twój skopiowany token API>
|
19 |
API_TOKEN = os.getenv("HUGGINGFACE_API_TOKEN")
|
20 |
|
21 |
# Sprawdzenie, czy token został załadowany
|
22 |
if not API_TOKEN:
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
# Endpoint modelu InstantID na Hugging Face Inference API
|
28 |
-
API_URL = "https://api-inference.huggingface.co/models/InstantX/InstantID"
|
29 |
|
30 |
# Profesjonalny prompt, który chcemy uzyskać
|
31 |
-
# Możesz go później dostosować
|
32 |
LINKEDIN_PROMPT = (
|
33 |
"linkedin professional profile photo, corporate headshot, high quality, realistic photograph, "
|
34 |
-
"
|
35 |
"soft studio lighting, sharp focus, looking at camera, slight smile, natural skin texture"
|
36 |
)
|
37 |
|
38 |
-
|
39 |
-
#
|
40 |
-
|
|
|
|
|
|
|
|
|
41 |
|
42 |
# --- Logika aplikacji ---
|
43 |
def generate_photo(input_selfie, current_prompt):
|
|
|
44 |
print("\n--- Funkcja generate_photo (gradio_client) została wywołana ---")
|
45 |
|
46 |
# 1. Walidacja danych wejściowych
|
47 |
if input_selfie is None:
|
48 |
-
print("
|
49 |
-
raise gr.Error("Proszę najpierw wgrać swoje selfie!")
|
50 |
if not current_prompt:
|
51 |
-
print("
|
52 |
-
raise gr.Error("Prompt nie może być pusty!")
|
53 |
-
#
|
54 |
if not API_TOKEN:
|
55 |
-
print("
|
56 |
-
# Nie rzucamy błędu, spróbujemy połączyć się anonimowo
|
57 |
|
58 |
print(f"Otrzymano obrazek typu: {type(input_selfie)}, Rozmiar: {input_selfie.size}")
|
59 |
print(f"Otrzymano prompt (początek): {current_prompt[:100]}...")
|
60 |
|
61 |
# 2. Przygotowanie pliku tymczasowego dla obrazka wejściowego
|
62 |
temp_dir = f"temp_input_{uuid.uuid4()}" # Unikalny folder tymczasowy
|
63 |
-
|
64 |
-
input_image_path = os.path.join(temp_dir, "input_selfie.jpg")
|
65 |
try:
|
|
|
|
|
|
|
|
|
66 |
# Upewnijmy się, że obraz jest w RGB przed zapisem jako JPEG
|
|
|
67 |
rgb_image = input_selfie
|
68 |
if input_selfie.mode == 'RGBA':
|
69 |
print("Konwertuję obraz RGBA na RGB z białym tłem przed zapisem...")
|
|
|
70 |
rgb_image = Image.new("RGB", input_selfie.size, (255, 255, 255))
|
|
|
71 |
rgb_image.paste(input_selfie, mask=input_selfie.split()[3])
|
72 |
|
|
|
73 |
rgb_image.save(input_image_path, format="JPEG")
|
74 |
print(f"Obrazek wejściowy zapisany tymczasowo w: {input_image_path}")
|
|
|
75 |
except Exception as e:
|
76 |
print(f"BŁĄD podczas zapisywania obrazu tymczasowego: {e}")
|
77 |
-
traceback.print_exc()
|
78 |
-
# Spróbuj posprzątać folder tymczasowy
|
79 |
-
if os.path.exists(temp_dir):
|
80 |
-
|
|
|
|
|
|
|
|
|
|
|
81 |
raise gr.Error(f"Problem z przygotowaniem obrazu do wysłania: {e}")
|
82 |
|
83 |
# 3. Wywołanie zdalnego API Gradio za pomocą gradio_client
|
84 |
-
output_image = None
|
|
|
85 |
try:
|
|
|
86 |
print(f"Łączenie z docelowym Space Gradio: {TARGET_SPACE_ID}")
|
87 |
-
# Inicjalizujemy klienta, przekazując token (
|
88 |
client = Client(TARGET_SPACE_ID, hf_token=API_TOKEN)
|
89 |
-
# Możesz spróbować bez tokena, jeśli są problemy: client = Client(TARGET_SPACE_URL)
|
90 |
-
TARGET_SPACE_ID = "InstantX/InstantID"
|
91 |
-
|
92 |
-
print("Połączono. Próbuję wywołać funkcję predict...")
|
93 |
|
94 |
-
|
95 |
-
|
96 |
-
#
|
97 |
-
#
|
98 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
|
100 |
-
|
101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
|
|
|
103 |
result = client.predict(
|
104 |
-
file(input_image_path), #
|
105 |
-
None, #
|
106 |
-
current_prompt, #
|
107 |
-
negative_prompt, #
|
108 |
-
style_name, #
|
109 |
-
|
110 |
-
|
111 |
-
api_name=
|
112 |
-
# Sprawdź API docelowego space jeśli to nie działa! Może być "/predict"
|
113 |
-
# Można sprawdzić programowo: client.view_api(all_endpoints=True) i wydrukować w logach
|
114 |
)
|
115 |
|
116 |
-
#
|
117 |
-
# Dla InstantID zwykle jest to lista obrazów (nawet jeśli jest jeden).
|
118 |
-
# Często zwraca ścieżkę do pliku (lub listę ścieżek) zapisanego tymczasowo przez gradio_client.
|
119 |
print(f"Otrzymano wynik od klienta: {type(result)}")
|
120 |
-
|
|
|
121 |
|
122 |
-
#
|
123 |
if isinstance(result, list) and len(result) > 0 and isinstance(result[0], str) and os.path.exists(result[0]):
|
124 |
-
output_file_path = result[0]
|
125 |
-
print(f"
|
126 |
-
# Wczytaj obrazek wynikowy z tej ścieżki
|
127 |
output_image = Image.open(output_file_path)
|
128 |
-
print(f"Obrazek wynikowy załadowany. Rozmiar: {output_image.size}")
|
129 |
-
|
|
|
130 |
output_file_path = result
|
131 |
-
print(f"
|
132 |
output_image = Image.open(output_file_path)
|
133 |
-
print(f"Obrazek wynikowy załadowany. Rozmiar: {output_image.size}")
|
134 |
else:
|
135 |
-
# Jeśli wynik nie jest
|
136 |
print(f"BŁĄD: Otrzymano nieoczekiwany format wyniku od gradio_client: {type(result)}")
|
137 |
raise gr.Error(f"Nie udało się przetworzyć wyniku ze zdalnego API. Otrzymano: {str(result)[:200]}")
|
138 |
|
139 |
except Exception as e:
|
|
|
140 |
print(f"BŁĄD podczas komunikacji z klientem Gradio lub przetwarzania wyniku: {e}")
|
141 |
-
traceback.print_exc()
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
finally:
|
145 |
-
# 4. Sprzątanie -
|
146 |
-
if os.path.exists(temp_dir):
|
147 |
try:
|
148 |
shutil.rmtree(temp_dir)
|
149 |
print(f"Folder tymczasowy {temp_dir} usunięty.")
|
150 |
except Exception as e_clean:
|
|
|
151 |
print(f"OSTRZEŻENIE: Nie udało się usunąć folderu tymczasowego {temp_dir}: {e_clean}")
|
152 |
|
153 |
-
# 5. Zwróć wynik (załadowany obrazek PIL)
|
154 |
if output_image:
|
|
|
155 |
return output_image
|
156 |
else:
|
157 |
-
#
|
158 |
-
print("BŁĄD: Brak obrazka wynikowego po zakończeniu funkcji.")
|
159 |
-
raise gr.Error("Nie udało się uzyskać obrazka wynikowego.")
|
|
|
160 |
|
161 |
# --- Budowa Interfejsu Gradio ---
|
162 |
print("--- Definiowanie interfejsu Gradio ---")
|
|
|
163 |
with gr.Blocks(css="footer {display: none !important}") as demo: # Ukrywamy domyślny footer Gradio
|
|
|
164 |
gr.Markdown(
|
165 |
"""
|
166 |
# Generator Profesjonalnych Zdjęć Profilowych
|
167 |
-
Wgraj swoje selfie, a my postaramy się stworzyć profesjonalne zdjęcie w stylu LinkedIn!
|
168 |
-
**
|
|
|
|
|
|
|
169 |
"""
|
170 |
)
|
171 |
|
|
|
172 |
with gr.Row():
|
|
|
173 |
with gr.Column(scale=1):
|
|
|
174 |
input_image = gr.Image(
|
175 |
label="1. Wgraj swoje selfie (JPG/PNG)",
|
176 |
type="pil" # Chcemy obiekt PIL w funkcji Pythona
|
177 |
)
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
|
|
182 |
)
|
183 |
-
|
|
|
184 |
|
|
|
185 |
with gr.Column(scale=1):
|
|
|
186 |
output_image = gr.Image(
|
187 |
label="Oto Twoje wygenerowane zdjęcie:",
|
188 |
type="pil" # Oczekujemy obiektu PIL jako wynik
|
189 |
)
|
190 |
-
# Można dodać informację o stanie
|
191 |
-
status_info = gr.Textbox(label="Status", interactive=False)
|
192 |
|
193 |
# --- Podłączenie akcji do przycisku ---
|
194 |
-
#
|
195 |
-
# Przekaż zawartość 'input_image' jako argument do funkcji
|
196 |
-
# Wynik funkcji umieść w komponencie 'output_image'
|
197 |
generate_button.click(
|
198 |
-
fn=generate_photo,
|
199 |
-
inputs=[input_image, prompt_input],
|
200 |
-
outputs=[output_image]
|
201 |
-
# inputs=[input_image, prompt_input], # Jeśli chcemy przekazać też prompt
|
202 |
-
# outputs=[output_image, status_info] # Jeśli chcemy aktualizować też status
|
203 |
)
|
204 |
|
205 |
print("--- Interfejs Gradio zdefiniowany ---")
|
@@ -207,5 +251,8 @@ print("--- Interfejs Gradio zdefiniowany ---")
|
|
207 |
# --- Uruchomienie aplikacji ---
|
208 |
if __name__ == "__main__":
|
209 |
print("--- Uruchamianie demo.launch() ---")
|
210 |
-
|
211 |
-
|
|
|
|
|
|
|
|
1 |
import gradio as gr
|
2 |
+
# requests jest nadal w requirements.txt, ale nie jest już bezpośrednio używany do głównego wywołania API
|
3 |
+
import requests
|
4 |
+
from PIL import Image
|
5 |
+
import io
|
6 |
+
import os
|
7 |
+
import traceback
|
8 |
+
# Poprawny import dla gradio_client
|
9 |
from gradio_client import Client, file
|
10 |
import uuid
|
11 |
import shutil
|
|
|
14 |
|
15 |
# --- Konfiguracja ---
|
16 |
# Odczytanie sekretu (API Token) ustawionego w Space
|
|
|
|
|
|
|
|
|
17 |
API_TOKEN = os.getenv("HUGGINGFACE_API_TOKEN")
|
18 |
|
19 |
# Sprawdzenie, czy token został załadowany
|
20 |
if not API_TOKEN:
|
21 |
+
# Nadal ostrzegamy, ale klient może działać anonimowo
|
22 |
+
print("!!! OSTRZEŻENIE: Nie znaleziono sekretu HUGGINGFACE_API_TOKEN. Klient Gradio spróbuje połączyć się anonimowo (mogą obowiązywać limity). !!!")
|
23 |
+
else:
|
24 |
+
print("--- Sekret HUGGINGFACE_API_TOKEN załadowany. ---")
|
|
|
|
|
25 |
|
26 |
# Profesjonalny prompt, który chcemy uzyskać
|
|
|
27 |
LINKEDIN_PROMPT = (
|
28 |
"linkedin professional profile photo, corporate headshot, high quality, realistic photograph, "
|
29 |
+
"person wearing a dark business suit or elegant blouse, plain white background, " # Usunięto potencjalnie problematyczne tagi
|
30 |
"soft studio lighting, sharp focus, looking at camera, slight smile, natural skin texture"
|
31 |
)
|
32 |
|
33 |
+
# Adres publicznego Space'a z InstantID
|
34 |
+
# Używamy identyfikatora Space'a dla gradio_client
|
35 |
+
TARGET_SPACE_ID = "InstantX/InstantID"
|
36 |
+
|
37 |
+
# Kontrolny print, aby sprawdzić, czy zmienna jest ładowana na starcie
|
38 |
+
# Powinien pojawić się w logach kontenera po restarcie Space'a
|
39 |
+
print(f"--- Konfiguracja załadowana. Cel dla gradio_client: {TARGET_SPACE_ID} ---")
|
40 |
|
41 |
# --- Logika aplikacji ---
|
42 |
def generate_photo(input_selfie, current_prompt):
|
43 |
+
# Ta funkcja jest teraz wywoływana przez Gradio po kliknięciu przycisku
|
44 |
print("\n--- Funkcja generate_photo (gradio_client) została wywołana ---")
|
45 |
|
46 |
# 1. Walidacja danych wejściowych
|
47 |
if input_selfie is None:
|
48 |
+
print("BŁĄD: Nie wgrano zdjęcia wejściowego.")
|
49 |
+
raise gr.Error("Proszę najpierw wgrać swoje selfie!") # Pokaż błąd w interfejsie
|
50 |
if not current_prompt:
|
51 |
+
print("BŁĄD: Prompt jest pusty.")
|
52 |
+
raise gr.Error("Prompt (opis zdjęcia) nie może być pusty!")
|
53 |
+
# Informacja o tokenie (nie jest już krytyczny dla działania, ale może wpływać na limity)
|
54 |
if not API_TOKEN:
|
55 |
+
print("INFO: Brak API Tokena. Połączenie z publicznym Space jako anonimowy użytkownik.")
|
|
|
56 |
|
57 |
print(f"Otrzymano obrazek typu: {type(input_selfie)}, Rozmiar: {input_selfie.size}")
|
58 |
print(f"Otrzymano prompt (początek): {current_prompt[:100]}...")
|
59 |
|
60 |
# 2. Przygotowanie pliku tymczasowego dla obrazka wejściowego
|
61 |
temp_dir = f"temp_input_{uuid.uuid4()}" # Unikalny folder tymczasowy
|
62 |
+
input_image_path = None # Inicjalizacja na wypadek błędu przed przypisaniem
|
|
|
63 |
try:
|
64 |
+
os.makedirs(temp_dir, exist_ok=True)
|
65 |
+
# Pełna ścieżka do pliku tymczasowego
|
66 |
+
input_image_path = os.path.join(temp_dir, "input_selfie.jpg")
|
67 |
+
|
68 |
# Upewnijmy się, że obraz jest w RGB przed zapisem jako JPEG
|
69 |
+
# To ważne, bo format JPEG nie obsługuje przezroczystości (kanału alfa)
|
70 |
rgb_image = input_selfie
|
71 |
if input_selfie.mode == 'RGBA':
|
72 |
print("Konwertuję obraz RGBA na RGB z białym tłem przed zapisem...")
|
73 |
+
# Stwórz nowy obraz RGB wypełniony białym kolorem
|
74 |
rgb_image = Image.new("RGB", input_selfie.size, (255, 255, 255))
|
75 |
+
# Wklej oryginalny obraz używając jego kanału alfa jako maski
|
76 |
rgb_image.paste(input_selfie, mask=input_selfie.split()[3])
|
77 |
|
78 |
+
# Zapisz przetworzony obraz jako JPEG
|
79 |
rgb_image.save(input_image_path, format="JPEG")
|
80 |
print(f"Obrazek wejściowy zapisany tymczasowo w: {input_image_path}")
|
81 |
+
|
82 |
except Exception as e:
|
83 |
print(f"BŁĄD podczas zapisywania obrazu tymczasowego: {e}")
|
84 |
+
traceback.print_exc() # Pokaż pełny błąd w logach
|
85 |
+
# Spróbuj posprzątać folder tymczasowy nawet jeśli zapis się nie udał
|
86 |
+
if temp_dir and os.path.exists(temp_dir):
|
87 |
+
try:
|
88 |
+
shutil.rmtree(temp_dir)
|
89 |
+
print(f"Folder tymczasowy {temp_dir} usunięty po błędzie zapisu.")
|
90 |
+
except Exception as e_clean:
|
91 |
+
print(f"OSTRZEŻENIE: Nie udało się usunąć folderu tymczasowego {temp_dir} po błędzie zapisu: {e_clean}")
|
92 |
+
# Pokaż błąd użytkownikowi
|
93 |
raise gr.Error(f"Problem z przygotowaniem obrazu do wysłania: {e}")
|
94 |
|
95 |
# 3. Wywołanie zdalnego API Gradio za pomocą gradio_client
|
96 |
+
output_image = None # Zmienna na wynikowy obrazek
|
97 |
+
client = None # Zmienna na obiekt klienta
|
98 |
try:
|
99 |
+
# Używamy TARGET_SPACE_ID zdefiniowanej globalnie
|
100 |
print(f"Łączenie z docelowym Space Gradio: {TARGET_SPACE_ID}")
|
101 |
+
# Inicjalizujemy klienta, przekazując ID Space'a i token (jeśli jest)
|
102 |
client = Client(TARGET_SPACE_ID, hf_token=API_TOKEN)
|
|
|
|
|
|
|
|
|
103 |
|
104 |
+
print("Połączono. Próbuję wywołać funkcję na zdalnym Space...")
|
105 |
+
|
106 |
+
# --- Konfiguracja parametrów dla InstantX/InstantID ---
|
107 |
+
# Te wartości mogą wymagać dostosowania w zależności od dokładnej
|
108 |
+
# konfiguracji zdalnego Space'a i pożądanych efektów.
|
109 |
+
negative_prompt = "ugly, deformed, noisy, blurry, low contrast, text, signature, watermark, duplicate, multiple people, cartoon, drawing, illustration, sketch"
|
110 |
+
# Popularne style w demo InstantID: Realistic, (No style), Comic book, Disney, Pixar
|
111 |
+
style_name = "Realistic"
|
112 |
+
# Skale ControlNet i IP-Adapter (siła tożsamości) - typowe wartości to 0.6-1.0
|
113 |
+
cn_scale = 0.8
|
114 |
+
ip_scale = 0.8
|
115 |
+
# Nazwa endpointu API w zdalnym Space'u (często '/predict' lub specyficzna jak '/generate_image')
|
116 |
+
# Dla InstantX/InstantID wydaje się, że to '/generate_image'
|
117 |
+
api_endpoint_name = "/generate_image"
|
118 |
+
# -------------------------------------------------------
|
119 |
|
120 |
+
print(f"Wywołuję endpoint '{api_endpoint_name}' z parametrami:")
|
121 |
+
print(f" Input image path: {input_image_path}")
|
122 |
+
# Nie drukujemy całego promptu, bo może być długi
|
123 |
+
print(f" Prompt (start): {current_prompt[:60]}...")
|
124 |
+
print(f" Negative Prompt (start): {negative_prompt[:60]}...")
|
125 |
+
print(f" Style: {style_name}")
|
126 |
+
print(f" ControlNet Scale: {cn_scale}")
|
127 |
+
print(f" IP-Adapter Scale: {ip_scale}")
|
128 |
|
129 |
+
# Wywołanie funkcji 'predict' na zdalnym kliencie
|
130 |
result = client.predict(
|
131 |
+
file(input_image_path), # Obraz twarzy (jako obiekt pliku Gradio)
|
132 |
+
None, # Obraz pozy (opcjonalny, dajemy None)
|
133 |
+
current_prompt, # Prompt tekstowy
|
134 |
+
negative_prompt, # Negatywny prompt
|
135 |
+
style_name, # Nazwa stylu
|
136 |
+
cn_scale, # Skala ControlNet
|
137 |
+
ip_scale, # Skala IP-Adapter (siła tożsamości)
|
138 |
+
api_name=api_endpoint_name # Nazwa endpointu API
|
|
|
|
|
139 |
)
|
140 |
|
141 |
+
# Przetwarzanie wyniku zwróconego przez klienta
|
|
|
|
|
142 |
print(f"Otrzymano wynik od klienta: {type(result)}")
|
143 |
+
# Wydrukuj fragment wyniku, aby zobaczyć jego strukturę
|
144 |
+
print(f"Wynik (fragment): {str(result)[:500]}")
|
145 |
|
146 |
+
# Sprawdzamy, czy wynik jest listą ścieżek do plików (typowe dla Gradio)
|
147 |
if isinstance(result, list) and len(result) > 0 and isinstance(result[0], str) and os.path.exists(result[0]):
|
148 |
+
output_file_path = result[0] # Bierzemy pierwszą ścieżkę z listy
|
149 |
+
print(f"Przetwarzam pierwszy obrazek wynikowy ze ścieżki: {output_file_path}")
|
150 |
+
# Wczytaj obrazek wynikowy z tej ścieżki za pomocą Pillow
|
151 |
output_image = Image.open(output_file_path)
|
152 |
+
print(f"Obrazek wynikowy załadowany pomyślnie. Rozmiar: {output_image.size}")
|
153 |
+
# Sprawdzamy, czy wynik jest pojedynczą ścieżką do pliku
|
154 |
+
elif isinstance(result, str) and os.path.exists(result):
|
155 |
output_file_path = result
|
156 |
+
print(f"Przetwarzam obrazek wynikowy ze ścieżki: {output_file_path}")
|
157 |
output_image = Image.open(output_file_path)
|
158 |
+
print(f"Obrazek wynikowy załadowany pomyślnie. Rozmiar: {output_image.size}")
|
159 |
else:
|
160 |
+
# Jeśli wynik nie jest ani listą ścieżek, ani pojedynczą ścieżką
|
161 |
print(f"BŁĄD: Otrzymano nieoczekiwany format wyniku od gradio_client: {type(result)}")
|
162 |
raise gr.Error(f"Nie udało się przetworzyć wyniku ze zdalnego API. Otrzymano: {str(result)[:200]}")
|
163 |
|
164 |
except Exception as e:
|
165 |
+
# Obsługa wszelkich błędów podczas komunikacji z klientem lub przetwarzania wyniku
|
166 |
print(f"BŁĄD podczas komunikacji z klientem Gradio lub przetwarzania wyniku: {e}")
|
167 |
+
traceback.print_exc() # Pokaż pełny błąd w logach
|
168 |
+
# Spróbuj dać użytkownikowi bardziej pomocny komunikat
|
169 |
+
error_message = f"Problem podczas generowania przez zdalny serwis InstantID: {e}"
|
170 |
+
if "Could not fetch config" in str(e):
|
171 |
+
error_message = f"Nie można połączyć się z konfiguracją zdalnego serwisu ({TARGET_SPACE_ID}). Może być chwilowo niedostępny, przeciążony lub wymagać logowania. Spróbuj ponownie później."
|
172 |
+
elif "timed out" in str(e).lower():
|
173 |
+
error_message = "Przekroczono limit czasu oczekiwania na odpowiedź ze zdalnego serwisu. Może być przeciążony. Spróbuj ponownie."
|
174 |
+
elif "queue full" in str(e).lower():
|
175 |
+
error_message = "Kolejka w zdalnym serwisie jest pełna. Spróbuj ponownie za chwilę."
|
176 |
+
# Pokaż błąd w interfejsie Gradio
|
177 |
+
raise gr.Error(error_message)
|
178 |
|
179 |
finally:
|
180 |
+
# 4. Sprzątanie - ZAWSZE próbuj usunąć folder tymczasowy, nawet jeśli był błąd
|
181 |
+
if temp_dir and os.path.exists(temp_dir):
|
182 |
try:
|
183 |
shutil.rmtree(temp_dir)
|
184 |
print(f"Folder tymczasowy {temp_dir} usunięty.")
|
185 |
except Exception as e_clean:
|
186 |
+
# Tylko ostrzeżenie, jeśli sprzątanie się nie uda
|
187 |
print(f"OSTRZEŻENIE: Nie udało się usunąć folderu tymczasowego {temp_dir}: {e_clean}")
|
188 |
|
189 |
+
# 5. Zwróć wynik (załadowany obrazek PIL), jeśli się udało
|
190 |
if output_image:
|
191 |
+
print("Zwracam wygenerowany obrazek do interfejsu Gradio.")
|
192 |
return output_image
|
193 |
else:
|
194 |
+
# Ten kod nie powinien zostać osiągnięty, jeśli błędy są poprawnie obsługiwane
|
195 |
+
print("BŁĄD KRYTYCZNY: Brak obrazka wynikowego po zakończeniu funkcji, a nie zgłoszono błędu.")
|
196 |
+
raise gr.Error("Nie udało się uzyskać obrazka wynikowego z nieznanego powodu.")
|
197 |
+
|
198 |
|
199 |
# --- Budowa Interfejsu Gradio ---
|
200 |
print("--- Definiowanie interfejsu Gradio ---")
|
201 |
+
# Tworzymy główny blok aplikacji Gradio
|
202 |
with gr.Blocks(css="footer {display: none !important}") as demo: # Ukrywamy domyślny footer Gradio
|
203 |
+
# Tytuł i opis aplikacji wyświetlany użytkownikowi
|
204 |
gr.Markdown(
|
205 |
"""
|
206 |
# Generator Profesjonalnych Zdjęć Profilowych
|
207 |
+
Wgraj swoje selfie, a my (korzystając z modelu InstantID) postaramy się stworzyć profesjonalne zdjęcie w stylu LinkedIn!
|
208 |
+
**Wskazówki:**
|
209 |
+
* Użyj wyraźnego zdjęcia twarzy, patrzącej w miarę prosto, dobrze oświetlonej.
|
210 |
+
* Unikaj zdjęć grupowych, bardzo małych lub z mocno zasłoniętą twarzą.
|
211 |
+
* Generowanie może potrwać od 30 sekund do kilku minut, bądź cierpliwy!
|
212 |
"""
|
213 |
)
|
214 |
|
215 |
+
# Układ interfejsu w dwóch kolumnach
|
216 |
with gr.Row():
|
217 |
+
# Kolumna lewa (wejście)
|
218 |
with gr.Column(scale=1):
|
219 |
+
# Komponent do wgrywania obrazka
|
220 |
input_image = gr.Image(
|
221 |
label="1. Wgraj swoje selfie (JPG/PNG)",
|
222 |
type="pil" # Chcemy obiekt PIL w funkcji Pythona
|
223 |
)
|
224 |
+
# Pole tekstowe do wpisania promptu (opisu zdjęcia)
|
225 |
+
prompt_input = gr.Textbox(
|
226 |
+
label="2. Opis pożądanego zdjęcia (prompt)",
|
227 |
+
value=LINKEDIN_PROMPT, # Ustawiamy domyślny prompt
|
228 |
+
lines=4 # Określamy wysokość pola
|
229 |
)
|
230 |
+
# Przycisk uruchamiający generowanie
|
231 |
+
generate_button = gr.Button("✨ Generuj Zdjęcie Biznesowe ✨", variant="primary") # Wyróżniony przycisk
|
232 |
|
233 |
+
# Kolumna prawa (wyjście)
|
234 |
with gr.Column(scale=1):
|
235 |
+
# Komponent do wyświetlania wyniku (obrazka)
|
236 |
output_image = gr.Image(
|
237 |
label="Oto Twoje wygenerowane zdjęcie:",
|
238 |
type="pil" # Oczekujemy obiektu PIL jako wynik
|
239 |
)
|
|
|
|
|
240 |
|
241 |
# --- Podłączenie akcji do przycisku ---
|
242 |
+
# Definiujemy, co ma się stać po kliknięciu przycisku 'generate_button'
|
|
|
|
|
243 |
generate_button.click(
|
244 |
+
fn=generate_photo, # Wywołaj funkcję 'generate_photo'
|
245 |
+
inputs=[input_image, prompt_input], # Przekaż zawartość 'input_image' i 'prompt_input' jako argumenty
|
246 |
+
outputs=[output_image] # Wynik funkcji umieść w komponencie 'output_image'
|
|
|
|
|
247 |
)
|
248 |
|
249 |
print("--- Interfejs Gradio zdefiniowany ---")
|
|
|
251 |
# --- Uruchomienie aplikacji ---
|
252 |
if __name__ == "__main__":
|
253 |
print("--- Uruchamianie demo.launch() ---")
|
254 |
+
# Uruchamiamy aplikację Gradio
|
255 |
+
# share=False: nie generuj publicznego linku (zalecane dla bezpieczeństwa)
|
256 |
+
# debug=False: wyłącz tryb debugowania Gradio (zalecane dla produkcji)
|
257 |
+
demo.launch(share=False, debug=False)
|
258 |
+
print("--- Aplikacja Gradio zakończyła działanie (jeśli nie jest w trybie ciągłym serwera) ---")
|