hazelhh commited on
Commit
905e639
·
verified ·
1 Parent(s): f98663a

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +72 -16
main.py CHANGED
@@ -39,6 +39,7 @@ user_states = defaultdict(lambda: {
39
 
40
  MAX_IMAGES_PER_TYPE = 3
41
  GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent"
 
42
  GOOGLE_API_KEY = os.environ["GOOGLE_API_KEY"]
43
  IMGBB_API_KEY = os.environ["IMGBB_API_KEY"]
44
 
@@ -97,7 +98,19 @@ def upload_to_imgbb(image_base64: str):
97
  logging.error(f"Imgbb API Response: {response.status_code} - {response.text}")
98
  return None
99
 
100
- def get_gemini_response(prompt: str, images: list):
 
 
 
 
 
 
 
 
 
 
 
 
101
  logging.info("Sending request to Gemini API.")
102
  payload = {
103
  "contents": [
@@ -112,7 +125,7 @@ def get_gemini_response(prompt: str, images: list):
112
  }
113
 
114
  try:
115
- response = requests.post(f"{GEMINI_API_URL}?key={GOOGLE_API_KEY}", json=payload, timeout=60)
116
  response.raise_for_status()
117
  response_data = response.json()
118
  if 'candidates' in response_data and response_data['candidates']:
@@ -127,15 +140,57 @@ def get_gemini_response(prompt: str, images: list):
127
  return f"Gemini API 請求失敗:{e}"
128
 
129
 
130
- def get_virtual_tryon_image(user_photo, upper_body_image, lower_body_image):
131
  """
132
- 模擬虛擬試穿 API,返回一個試穿後的圖片 URL。
133
  """
134
- logging.info("Simulating virtual try-on and returning a placeholder image.")
135
- # 這裡的邏輯需要您自行實現,可以是呼叫一個公開的 API,
136
- # 或者將圖片傳輸到一個運行 IDM-VTON 的伺服器。
137
- # 由於沒有實際的 IDM-VTON 服務,我們使用佔位符圖片來展示功能。
138
- return "https://placehold.co/1024x1024?text=Virtual+Try-On+Outfit"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
  @line_handler.add(MessageEvent, message=TextMessage)
141
  def handle_text_message(event):
@@ -196,7 +251,7 @@ def handle_text_message(event):
196
  TextSendMessage(text=f"請上傳三件上衣圖片,您已上傳 {len(user_states[user_id]['upper_body_images'])}/{MAX_IMAGES_PER_TYPE} 張。")
197
  )
198
  return
199
- elif text == "褲子" or text == "裙子":
200
  user_states[user_id]["current_mode"] = "lower"
201
  line_bot_api.reply_message(
202
  reply_token,
@@ -262,7 +317,7 @@ def handle_image_message(event):
262
  [user_states[user_id]["personal_photo_base64"]]
263
  )
264
 
265
- response_text = get_gemini_response(prompt, all_images_base64)
266
 
267
  # 從 Gemini 建議中找出最佳搭配的衣物圖片(這裡需要更複雜的邏輯,我們使用佔位符)
268
  # 在實際應用中,您可以設計一個提示,讓 Gemini 返回最佳搭配的圖片索引
@@ -273,7 +328,7 @@ def handle_image_message(event):
273
  best_lower_body_image = user_states[user_id]["lower_body_images"][best_lower_index]
274
 
275
  # 呼叫虛擬試穿 API,並取得結果圖片 URL
276
- virtual_tryon_url = get_virtual_tryon_image(user_states[user_id]["personal_photo"], best_upper_body_image, best_lower_body_image)
277
 
278
  # 發送 Gemini 的文字建議
279
  line_bot_api.push_message(
@@ -282,10 +337,11 @@ def handle_image_message(event):
282
  )
283
 
284
  # 發送虛擬試穿照片
285
- line_bot_api.push_message(
286
- user_id,
287
- ImageSendMessage(original_content_url=virtual_tryon_url, preview_image_url=virtual_tryon_url)
288
- )
 
289
 
290
  # 重置狀態以便下一次使用
291
  user_states[user_id] = defaultdict(lambda: {"upper_body_images": [], "lower_body_images": [], "current_mode": None, "is_ready_for_outfit": False, "is_ready_for_photo": False, "user_info": {}, "personal_photo": None, "personal_photo_base64": None})[user_id]
 
39
 
40
  MAX_IMAGES_PER_TYPE = 3
41
  GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent"
42
+ IMAGIN_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent"
43
  GOOGLE_API_KEY = os.environ["GOOGLE_API_KEY"]
44
  IMGBB_API_KEY = os.environ["IMGBB_API_KEY"]
45
 
 
98
  logging.error(f"Imgbb API Response: {response.status_code} - {response.text}")
99
  return None
100
 
101
+ def get_base64_from_url(image_url: str):
102
+ """
103
+ 從 URL 下載圖片並轉換為 Base64 編碼。
104
+ """
105
+ try:
106
+ response = requests.get(image_url)
107
+ response.raise_for_status()
108
+ return base64.b64encode(response.content).decode('utf-8')
109
+ except requests.exceptions.RequestException as e:
110
+ logging.error(f"Error fetching image from URL: {image_url}, error: {e}")
111
+ return None
112
+
113
+ def get_gemini_response(prompt: str, images: list, api_url: str):
114
  logging.info("Sending request to Gemini API.")
115
  payload = {
116
  "contents": [
 
125
  }
126
 
127
  try:
128
+ response = requests.post(f"{api_url}?key={GOOGLE_API_KEY}", json=payload, timeout=120) # Increased timeout for image generation
129
  response.raise_for_status()
130
  response_data = response.json()
131
  if 'candidates' in response_data and response_data['candidates']:
 
140
  return f"Gemini API 請求失敗:{e}"
141
 
142
 
143
+ def get_virtual_tryon_image(user_photo_base64: str, upper_body_image_base64: str, lower_body_image_base64: str):
144
  """
145
+ 使用 gemini-2.5-flash-image-preview (nanobanana) 模擬虛擬試穿。
146
  """
147
+ logging.info("Attempting virtual try-on with gemini-2.5-flash-image-preview.")
148
+
149
+ prompt = "請將提供的上衣和褲子,虛擬試穿到第一張人像照片上。結果必須看起來真實且自然,衣服的紋理和細節必須保留。"
150
+
151
+ all_images_base64 = [user_photo_base64, upper_body_image_base64, lower_body_image_base64]
152
+
153
+ payload = {
154
+ "contents": [
155
+ {
156
+ "parts": [
157
+ {"text": prompt}
158
+ ] + [
159
+ {"inlineData": {"mimeType": "image/jpeg", "data": img_base64}} for img_base64 in all_images_base64
160
+ ]
161
+ }
162
+ ],
163
+ "generationConfig": {
164
+ "responseModalities": ['IMAGE']
165
+ }
166
+ }
167
+
168
+ try:
169
+ response = requests.post(f"{IMAGIN_API_URL}?key={GOOGLE_API_KEY}", json=payload, timeout=120)
170
+ response.raise_for_status()
171
+ response_data = response.json()
172
+ if 'candidates' in response_data and response_data['candidates']:
173
+ # 從回應中提取 Base64 編碼的圖片
174
+ image_part = response_data['candidates'][0]['content']['parts'][0]
175
+ if image_part and 'inlineData' in image_part:
176
+ generated_image_base64 = image_part['inlineData']['data']
177
+ # 將生成的 Base64 圖片上傳到 Imgbb
178
+ virtual_tryon_url = upload_to_imgbb(generated_image_base64)
179
+ if virtual_tryon_url:
180
+ logging.info(f"Successfully generated and uploaded virtual try-on image: {virtual_tryon_url}")
181
+ return virtual_tryon_url
182
+ else:
183
+ logging.error("Failed to upload generated image to Imgbb.")
184
+ return None
185
+ else:
186
+ logging.error("Gemini image response format is invalid.")
187
+ return None
188
+ else:
189
+ logging.error(f"Gemini image generation response has no candidates: {response.text}")
190
+ return None
191
+ except requests.exceptions.RequestException as e:
192
+ logging.error(f"Gemini image generation API request failed: {e}")
193
+ return None
194
 
195
  @line_handler.add(MessageEvent, message=TextMessage)
196
  def handle_text_message(event):
 
251
  TextSendMessage(text=f"請上傳三件上衣圖片,您已上傳 {len(user_states[user_id]['upper_body_images'])}/{MAX_IMAGES_PER_TYPE} 張。")
252
  )
253
  return
254
+ elif text in ["褲子", "裙子"]:
255
  user_states[user_id]["current_mode"] = "lower"
256
  line_bot_api.reply_message(
257
  reply_token,
 
317
  [user_states[user_id]["personal_photo_base64"]]
318
  )
319
 
320
+ response_text = get_gemini_response(prompt, all_images_base64, GEMINI_API_URL)
321
 
322
  # 從 Gemini 建議中找出最佳搭配的衣物圖片(這裡需要更複雜的邏輯,我們使用佔位符)
323
  # 在實際應用中,您可以設計一個提示,讓 Gemini 返回最佳搭配的圖片索引
 
328
  best_lower_body_image = user_states[user_id]["lower_body_images"][best_lower_index]
329
 
330
  # 呼叫虛擬試穿 API,並取得結果圖片 URL
331
+ virtual_tryon_url = get_virtual_tryon_image(user_states[user_id]["personal_photo_base64"], get_base64_from_url(best_upper_body_image), get_base64_from_url(best_lower_body_image))
332
 
333
  # 發送 Gemini 的文字建議
334
  line_bot_api.push_message(
 
337
  )
338
 
339
  # 發送虛擬試穿照片
340
+ if virtual_tryon_url:
341
+ line_bot_api.push_message(
342
+ user_id,
343
+ ImageSendMessage(original_content_url=virtual_tryon_url, preview_image_url=virtual_tryon_url)
344
+ )
345
 
346
  # 重置狀態以便下一次使用
347
  user_states[user_id] = defaultdict(lambda: {"upper_body_images": [], "lower_body_images": [], "current_mode": None, "is_ready_for_outfit": False, "is_ready_for_photo": False, "user_info": {}, "personal_photo": None, "personal_photo_base64": None})[user_id]