PaulinaAa commited on
Commit
e7d12ee
·
verified ·
1 Parent(s): a66b82a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -102
app.py CHANGED
@@ -4,6 +4,9 @@ from PIL import Image # Dodajemy od razu
4
  import io # Dodajemy od razu
5
  import os # Do odczytu sekretu
6
  import traceback # Do lepszego śledzenia błędów
 
 
 
7
 
8
  print("--- Plik app.py - Start ładowania ---")
9
 
@@ -31,13 +34,15 @@ LINKEDIN_PROMPT = (
31
  "man/woman [choose one or remove] wearing a dark business suit or elegant blouse, plain white background, "
32
  "soft studio lighting, sharp focus, looking at camera, slight smile, natural skin texture"
33
  )
 
 
34
  # Uwaga: InstantID może nie być idealny w zmianie ubrań, skupia się na twarzy.
35
  # Prompt pomaga nadać ogólny styl.
36
 
37
  # --- Logika aplikacji ---
38
- # Na razie pusta funkcja, którą podłączymy do przycisku
39
- def generate_photo(input_selfie, current_prompt): # Dodajemy prompt jako wejście
40
- print("\n--- Funkcja generate_photo została wywołana ---")
41
  # 1. Walidacja danych wejściowych
42
  if input_selfie is None:
43
  print("Błąd: Nie wgrano zdjęcia wejściowego.")
@@ -45,118 +50,112 @@ def generate_photo(input_selfie, current_prompt): # Dodajemy prompt jako wejści
45
  if not current_prompt:
46
  print("Błąd: Prompt jest pusty.")
47
  raise gr.Error("Prompt nie może być pusty!")
 
48
  if not API_TOKEN:
49
- print("KRYTYCZNY BŁĄD: Brak API Tokena!")
50
- raise gr.Error("Błąd konfiguracji serwera: Brak API Tokena. Skontaktuj się z administratorem.")
51
 
52
  print(f"Otrzymano obrazek typu: {type(input_selfie)}, Rozmiar: {input_selfie.size}")
53
  print(f"Otrzymano prompt (początek): {current_prompt[:100]}...")
54
- print("Przygotowuję dane do wysłania do API...")
55
 
56
- # 2. Przygotowanie danych do wysłania (Obrazek jako bajty)
 
 
 
57
  try:
58
- buffered = io.BytesIO()
59
- # Sprawdźmy format i ewentualnie skonwertujmy tło dla PNG z przezroczystością
60
- image_format = "JPEG" # Preferowany format dla wielu API
61
- processed_image = input_selfie
62
  if input_selfie.mode == 'RGBA':
63
- print("Konwertuję obraz RGBA na RGB z białym tłem...")
64
- processed_image = Image.new("RGB", input_selfie.size, (255, 255, 255))
65
- processed_image.paste(input_selfie, mask=input_selfie.split()[3]) # Wklej z maską alfa
66
 
67
- processed_image.save(buffered, format=image_format)
68
- buffered.seek(0)
69
- print(f"Obrazek przekonwertowany do formatu {image_format} i zapisany w buforze.")
70
  except Exception as e:
71
- print(f"BŁĄD podczas przetwarzania obrazu: {e}")
72
- traceback.print_exc() # Wydrukuj pełny ślad błędu w logach
73
- raise gr.Error(f"Wystąpił błąd podczas przetwarzania Twojego zdjęcia: {e}")
74
-
75
- # 3. Przygotowanie nagłówków i danych dla API
76
- headers = {
77
- "Authorization": f"Bearer {API_TOKEN}"
78
- # Content-Type zostanie ustawiony automatycznie przez requests dla multipart/form-data
79
- }
80
-
81
- # InstantID (i wiele modeli image-to-image w Inference API) oczekuje
82
- # obrazu w 'files' i parametrów (jak prompt) w 'data'.
83
- # WAŻNE: Nazwa klucza dla obrazu ('image') i promptu ('prompt')
84
- # MUSI odpowiadać temu, czego oczekuje API modelu Sxela/instant-id.
85
- # Sprawdź dokumentację modelu na HF, jeśli to nie zadziała!
86
- files = {
87
- 'image': ('input_selfie.jpg', buffered, f'image/{image_format.lower()}')
88
- }
89
- payload = {
90
- 'prompt': current_prompt
91
- # Możesz tu dodać inne parametry wspierane przez model, np.:
92
- # 'negative_prompt': 'cartoon, drawing, illustration, sketch, duplicate heads, multiple people',
93
- # 'num_inference_steps': 30,
94
- # 'guidance_scale': 5.0,
95
- }
96
-
97
- print(f"Wysyłam zapytanie POST do: {API_URL}")
98
- print(f"Payload (parametry tekstowe): {payload}")
99
- # Nie drukujemy 'files', bo zawiera dane binarne
100
-
101
- # 4. Wywołanie API i obsługa odpowiedzi
102
- try:
103
- # Zwiększamy timeout, bo generowanie może chwilę potrwać
104
- response = requests.post(API_URL, headers=headers, data=payload, files=files, timeout=180) # 3 minuty timeout
105
-
106
- print(f"Otrzymano odpowiedź od API. Status: {response.status_code}")
107
- # Zawsze sprawdzaj content-type odpowiedzi!
108
- content_type = response.headers.get('Content-Type', '')
109
- print(f"Content-Type odpowiedzi: {content_type}")
110
-
111
- # Rzuć wyjątek dla złych statusów (4xx, 5xx)
112
- response.raise_for_status()
113
-
114
- # Sprawdź, czy odpowiedź to na pewno obrazek
115
- if 'image' not in content_type:
116
- # Jeśli API zwróciło sukces (200), ale nie obrazek, to prawdopodobnie błąd w JSON
117
- print(f"BŁĄD: API zwróciło status {response.status_code} ale nie obrazek (Content-Type: {content_type}). Treść:")
118
- # Spróbuj wydrukować treść jako tekst (może zawierać komunikat błędu)
119
- try:
120
- print(response.text[:1000]) # Pokaż do 1000 znaków
121
- raise gr.Error(f"API zwróciło nieoczekiwany format danych zamiast obrazka. Szczegóły: {response.text[:200]}")
122
- except Exception as json_err:
123
- print(f"Nie można odczytać treści odpowiedzi jako tekst: {json_err}")
124
- raise gr.Error(f"API zwróciło nieoczekiwany format danych (Content-Type: {content_type}).")
125
-
126
- # Jeśli status OK i Content-Type to obrazek, otwórz go
127
- print("Próbuję otworzyć obrazek z odpowiedzi API...")
128
- generated_image = Image.open(io.BytesIO(response.content))
129
- print(f"Sukces! Otworzono wygenerowany obrazek. Rozmiar: {generated_image.size}")
130
-
131
- # Zwróć wygenerowany obrazek do interfejsu Gradio
132
- return generated_image # Zwracamy obiekt PIL
133
-
134
- # Obsługa błędów z biblioteki requests (np. timeout, problem z połączeniem, błąd statusu z raise_for_status)
135
- except requests.exceptions.RequestException as e:
136
- print(f"BŁĄD podczas komunikacji z API: {e}")
137
- # Spróbujmy wyświetlić odpowiedź błędu z API, jeśli jest dostępna
138
- error_details = str(e)
139
- if e.response is not None:
140
- print(f"Odpowiedź błędu API (status {e.response.status_code}):")
141
- try:
142
- # Spróbuj zdekodować JSON jeśli to możliwe
143
- error_json = e.response.json()
144
- print(error_json)
145
- error_details = f"API Error (status {e.response.status_code}): {error_json.get('error', e.response.text)}"
146
- except ValueError: # Not JSON
147
- error_text = e.response.text[:500] # Pokaż fragment tekstu błędu
148
- print(error_text)
149
- error_details = f"API Error (status {e.response.status_code}): {error_text}"
150
-
151
  traceback.print_exc()
152
- raise gr.Error(f"Problem z połączeniem lub błąd API: {error_details}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
- # Obsługa innych, nieoczekiwanych błędów
155
  except Exception as e:
156
- print(f"Nieoczekiwany BŁĄD w funkcji generate_photo: {e}")
157
- traceback.print_exc() # Wypisz pełny ślad błędu w logach
158
- raise gr.Error(f"Wystąpił wewnętrzny błąd aplikacji: {e}")
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
  # --- Budowa Interfejsu Gradio ---
162
  print("--- Definiowanie interfejsu Gradio ---")
 
4
  import io # Dodajemy od razu
5
  import os # Do odczytu sekretu
6
  import traceback # Do lepszego śledzenia błędów
7
+ import gradio_client import Client, file
8
+ import uuid
9
+ import shutil
10
 
11
  print("--- Plik app.py - Start ładowania ---")
12
 
 
34
  "man/woman [choose one or remove] wearing a dark business suit or elegant blouse, plain white background, "
35
  "soft studio lighting, sharp focus, looking at camera, slight smile, natural skin texture"
36
  )
37
+
38
+ TARGET_SPACE_URL = "https://huggingface.co/spaces/InstantX/InstantID"
39
  # Uwaga: InstantID może nie być idealny w zmianie ubrań, skupia się na twarzy.
40
  # Prompt pomaga nadać ogólny styl.
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("Błąd: Nie wgrano zdjęcia wejściowego.")
 
50
  if not current_prompt:
51
  print("Błąd: Prompt jest pusty.")
52
  raise gr.Error("Prompt nie może być pusty!")
53
+ # Sprawdźmy też token, bo może być potrzebny do połączenia z klientem
54
  if not API_TOKEN:
55
+ print("OSTRZEŻENIE: Brak API Tokena. Połączenie z publicznym Space może działać, ale może być ograniczone.")
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
+ os.makedirs(temp_dir, exist_ok=True)
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
+ shutil.rmtree(temp_dir)
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_URL}")
87
+ # Inicjalizujemy klienta, przekazując token (może pomóc z limitami)
88
+ client = Client(TARGET_SPACE_URL, hf_token=API_TOKEN)
89
+ # Możesz spróbować bez tokena, jeśli są problemy: client = Client(TARGET_SPACE_URL)
90
+
91
+ print("Połączono. Próbuję wywołać funkcję predict...")
92
+
93
+ # WAŻNE: Musimy zgadnąć lub sprawdzić (np. przez client.view_api() lub patrząc na UI/API zdalnego Space'a),
94
+ # jakich dokładnie argumentów i w jakiej kolejności oczekuje funkcja 'predict' w zdalnym Space.
95
+ # Poniżej jest ZGADYWANA struktura bazująca na typowym demo InstantID. Może wymagać dostosowania!
96
+ # Argumenty: (Twarz, Poza, Prompt, NegPrompt, Styl, Skala CN, Skala IP)
97
+ # Używamy `file()` do przekazania ścieżki pliku obrazka.
98
+
99
+ negative_prompt = "ugly, deformed, noisy, blurry, low contrast, text, signature, watermark, duplicate, multiple people"
100
+ style_name = "Realistic" # Lub "(No style)", "Comic book" etc. - sprawdź opcje w docelowym Space!
101
+
102
+ result = client.predict(
103
+ file(input_image_path), # Argument 1: Obraz twarzy (jako plik)
104
+ None, # Argument 2: Obraz pozy (opcjonalny, dajemy None)
105
+ current_prompt, # Argument 3: Prompt
106
+ negative_prompt, # Argument 4: Negative prompt
107
+ style_name, # Argument 5: Nazwa stylu
108
+ 0.8, # Argument 6: Skala ControlNet (zgadnięta wartość domyślna)
109
+ 0.8, # Argument 7: Skala IdentityNet (IP Adapter) (zgadnięta wartość domyślna)
110
+ api_name="/generate_image" # Zwykle nazywa się '/predict', ale InstantX używa '/generate_image'
111
+ # Sprawdź API docelowego space jeśli to nie działa! Może być "/predict"
112
+ # Można sprawdzić programowo: client.view_api(all_endpoints=True) i wydrukować w logach
113
+ )
114
+
115
+ # `predict` zwraca wynik zgodny z wyjściami zdalnego API.
116
+ # Dla InstantID zwykle jest to lista obrazów (nawet jeśli jest jeden).
117
+ # Często zwraca ścieżkę do pliku (lub listę ścieżek) zapisanego tymczasowo przez gradio_client.
118
+ print(f"Otrzymano wynik od klienta: {type(result)}")
119
+ print(f"Wynik (fragment): {str(result)[:500]}") # Wydrukuj fragment wyniku
120
+
121
+ # Sprawdźmy, czy wynik to lista i czy zawiera ścieżkę do pliku
122
+ if isinstance(result, list) and len(result) > 0 and isinstance(result[0], str) and os.path.exists(result[0]):
123
+ output_file_path = result[0]
124
+ print(f"Wygląda na to, że otrzymano ścieżkę do obrazka: {output_file_path}")
125
+ # Wczytaj obrazek wynikowy z tej ścieżki
126
+ output_image = Image.open(output_file_path)
127
+ print(f"Obrazek wynikowy załadowany. Rozmiar: {output_image.size}")
128
+ elif isinstance(result, str) and os.path.exists(result): # Czasem zwraca tylko jedną ścieżkę
129
+ output_file_path = result
130
+ print(f"Wygląda na to, że otrzymano ścieżkę do obrazka: {output_file_path}")
131
+ output_image = Image.open(output_file_path)
132
+ print(f"Obrazek wynikowy załadowany. Rozmiar: {output_image.size}")
133
+ else:
134
+ # Jeśli wynik nie jest oczekiwaną ścieżką, zgłoś błąd
135
+ print(f"BŁĄD: Otrzymano nieoczekiwany format wyniku od gradio_client: {type(result)}")
136
+ raise gr.Error(f"Nie udało się przetworzyć wyniku ze zdalnego API. Otrzymano: {str(result)[:200]}")
137
 
 
138
  except Exception as e:
139
+ print(f"BŁĄD podczas komunikacji z klientem Gradio lub przetwarzania wyniku: {e}")
140
+ traceback.print_exc()
141
+ raise gr.Error(f"Problem podczas generowania przez zdalny serwis InstantID: {e}")
142
 
143
+ finally:
144
+ # 4. Sprzątanie - usuń folder tymczasowy z plikiem wejściowym
145
+ if os.path.exists(temp_dir):
146
+ try:
147
+ shutil.rmtree(temp_dir)
148
+ print(f"Folder tymczasowy {temp_dir} usunięty.")
149
+ except Exception as e_clean:
150
+ print(f"OSTRZEŻENIE: Nie udało się usunąć folderu tymczasowego {temp_dir}: {e_clean}")
151
+
152
+ # 5. Zwróć wynik (załadowany obrazek PIL)
153
+ if output_image:
154
+ return output_image
155
+ else:
156
+ # To nie powinno się zdarzyć, jeśli nie było błędu wcześniej, ale dla pewności:
157
+ print("BŁĄD: Brak obrazka wynikowego po zakończeniu funkcji.")
158
+ raise gr.Error("Nie udało się uzyskać obrazka wynikowego.")
159
 
160
  # --- Budowa Interfejsu Gradio ---
161
  print("--- Definiowanie interfejsu Gradio ---")