davidlee831117 commited on
Commit
e507f42
·
verified ·
1 Parent(s): cee002e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -99
app.py CHANGED
@@ -6,11 +6,10 @@ from io import BytesIO
6
  from PIL import Image
7
  from urllib.parse import urlparse, parse_qs
8
  import google.generativeai as genai
9
- import time
10
  import tempfile
11
- import uuid
12
-
13
- # --- 修正後的 API 設定與函式 ---
14
 
15
  # 全域變數來儲存 API 金鑰
16
  GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
@@ -26,12 +25,29 @@ print(f"Debug: Top-level Loaded GEMINI_API_KEY (first 5 chars): {GEMINI_API_KEY[
26
  if not GEMINI_API_KEY:
27
  raise ValueError("ERROR: GEMINI_API_KEY environment variable is not set. Please set it correctly.")
28
 
29
- # 配置 Gemini API,使用 genai.configure() 替代 genai.Client()
30
  genai.configure(api_key=GEMINI_API_KEY)
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  def generate_content(text, images, model="gemini-1.5-pro-latest"):
33
  """
34
- 使用官方 genai.generate_content 函式呼叫 API
 
35
  """
36
  try:
37
  # contents 是文字和圖片的列表
@@ -49,7 +65,6 @@ def generate_content(text, images, model="gemini-1.5-pro-latest"):
49
 
50
  # 檢查回應中是否有內容
51
  if not response.candidates:
52
- # 如果沒有候選內容,檢查是否有安全政策問題
53
  if hasattr(response, 'prompt_feedback') and 'safety_ratings' in response.prompt_feedback:
54
  for rating in response.prompt_feedback['safety_ratings']:
55
  if rating['block_reason']:
@@ -76,50 +91,88 @@ def generate_content(text, images, model="gemini-1.5-pro-latest"):
76
  print(f"Error calling Gemini API: {e}")
77
  return None, f"API 呼叫失敗: {e}"
78
 
79
- def load_uploaded_images(uploaded_files):
80
- """載入並顯示上傳的圖片。"""
81
- if not uploaded_files:
82
- return []
83
-
84
- loaded_images = []
85
- for file in uploaded_files:
86
- try:
87
- img = Image.open(file.name)
88
- if img.mode == "RGBA":
89
- img = img.convert("RGBA")
90
- loaded_images.append(img)
91
- except Exception as e:
92
- print(f"Failed to load image {file.name}: {e}")
93
- return loaded_images
94
-
95
- def process_image_and_prompt(uploaded_files, prompt, gemini_api_key):
96
- """處理圖片和提示詞,並呼叫 API。"""
97
  try:
98
- if not uploaded_files:
99
- raise gr.Error("請至少上傳一張圖片。", duration=5)
100
-
101
- images = load_uploaded_images(uploaded_files)
 
 
 
 
 
 
 
 
 
 
 
 
 
102
 
103
- # 這裡我們使用一個公開、穩定的多模態模型
104
- # gemini-1.5-pro-latest 支援圖片輸入
105
- model = "gemini-1.5-pro-latest"
106
-
107
- image_path, text_response = generate_content(text=prompt, images=images, model=model)
108
-
109
- uploaded_images = images # 確保畫廊顯示所有上傳的圖片
 
 
 
110
 
111
- if image_path:
112
- result_img = Image.open(image_path)
113
- if result_img.mode == "RGBA":
114
- result_img = result_img.convert("RGBA")
115
- return uploaded_images, [result_img], ""
116
- else:
117
- return uploaded_images, None, text_response
118
 
 
 
 
 
 
 
119
  except Exception as e:
120
- raise gr.Error(f"處理錯誤: {e}", duration=5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
- # --- Gradio 介面設定(這部分與你的程式碼完全相同)---
123
  with gr.Blocks(css_paths="style.css",) as demo:
124
  gr.HTML(
125
  """
@@ -128,7 +181,7 @@ with gr.Blocks(css_paths="style.css",) as demo:
128
  <img src="https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png" alt="Gemini logo">
129
  </div>
130
  <div>
131
- <h1>Gemini for Image Editing</h1>
132
  <p>Powered by <a href="https://gradio.app/">Gradio</a>⚡️|
133
  <a href="https://huggingface.co/spaces/ameerazam08/Gemini-Image-Edit?duplicate=true">Duplicate</a> this Repo |
134
  <a href="https://aistudio.google.com/apikey">Get an API Key</a> |
@@ -149,64 +202,48 @@ with gr.Blocks(css_paths="style.css",) as demo:
149
  with gr.Accordion("📌 Usage Instructions", open=False, elem_classes="instructions-accordion"):
150
  gr.Markdown("""
151
  ### 📌 Usage
152
- - Upload an image and enter a prompt to generate outputs.
153
- - If text is returned instead of an image, it will appear in the text output.
154
- - Upload Only PNG Image
155
- - ❌ **Do not use NSFW images!**
156
  """)
157
  with gr.Row(elem_classes="main-content"):
158
  with gr.Column(elem_classes="input-column"):
159
- image_input = gr.File(
160
- file_types=["image"],
161
- file_count="multiple",
162
- label="Upload Images ",
163
- elem_id="image-input",
164
- elem_classes="upload-box"
165
- )
166
- gemini_api_key = gr.Textbox(
167
- lines=1,
168
- placeholder="Enter Gemini API Key (optional)",
169
- label="Gemini API Key (optional)",
170
- elem_classes="api-key-input"
171
- )
172
- prompt_input = gr.Textbox(
173
- lines=2,
174
- placeholder="Enter prompt here...",
175
- label="Prompt",
176
- elem_classes="prompt-input"
177
- )
178
- submit_btn = gr.Button("Generate", elem_classes="generate-btn")
179
  with gr.Column(elem_classes="output-column"):
180
- uploaded_gallery = gr.Gallery(label="Uploaded Images", elem_classes="uploaded-gallery")
181
- output_gallery = gr.Gallery(label="Generated Outputs", elem_classes="output-gallery")
182
- output_text = gr.Textbox(
183
- label="Gemini Output",
184
- placeholder="Text response will appear here if no image is generated.",
185
- elem_classes="output-text"
186
  )
187
- submit_btn.click(
188
- fn=process_image_and_prompt,
189
- inputs=[image_input, prompt_input, gemini_api_key],
190
- outputs=[uploaded_gallery, output_gallery, output_text],
191
- )
192
- image_input.upload(
193
- fn=load_uploaded_images,
194
- inputs=[image_input],
195
- outputs=[uploaded_gallery],
 
 
 
 
 
 
 
 
196
  )
197
- gr.Markdown("## Try these examples", elem_classes="gr-examples-header")
198
- examples = [
199
- ["data/1.webp", 'change text to "AMEER"'],
200
- ["data/2.webp", "remove the spoon from hand only"],
201
- ["data/3.webp", 'change text to "Make it "'],
202
- ["data/1.jpg", "add joker style only on face"],
203
- ["data/1777043.jpg", "add lipstick on lip only"],
204
- ["data/76860.jpg", "add lipstick on lip only"],
205
- ["data/2807615.jpg", "make it happy looking face only"],
206
- ]
207
- gr.Examples(
208
- examples=examples,
209
- inputs=[image_input, prompt_input,],
210
- elem_id="examples-grid"
211
  )
 
212
  demo.queue(max_size=50).launch(mcp_server=True, share=True)
 
6
  from PIL import Image
7
  from urllib.parse import urlparse, parse_qs
8
  import google.generativeai as genai
 
9
  import tempfile
10
+ import time
11
+ import base64
12
+ import mimetypes
13
 
14
  # 全域變數來儲存 API 金鑰
15
  GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
 
25
  if not GEMINI_API_KEY:
26
  raise ValueError("ERROR: GEMINI_API_KEY environment variable is not set. Please set it correctly.")
27
 
28
+ # 配置 Gemini API,這是最簡潔穩定的方式
29
  genai.configure(api_key=GEMINI_API_KEY)
30
 
31
+ def load_image_from_url(url: str):
32
+ """從 URL 下載圖片並以 PIL Image 格式回傳。"""
33
+ try:
34
+ headers = {'User-Agent': 'Mozilla/5.0'}
35
+ response = requests.get(url, timeout=20, headers=headers)
36
+ response.raise_for_status() # 檢查請求是否成功
37
+ image = Image.open(BytesIO(response.content)).convert("RGB")
38
+ print(f"Debug: Successfully loaded image from URL: {url}")
39
+ return image
40
+ except requests.exceptions.HTTPError as e:
41
+ print(f"Error downloading image from {url}: HTTP Error {e.response.status_code}")
42
+ return None
43
+ except Exception as e:
44
+ print(f"An unexpected error occurred: {e}")
45
+ return None
46
+
47
  def generate_content(text, images, model="gemini-1.5-pro-latest"):
48
  """
49
+ 使用官方 genai.generate_content 函式呼叫 API
50
+ 這段邏輯來自你提供的 Hugging Face 範例,並已優化。
51
  """
52
  try:
53
  # contents 是文字和圖片的列表
 
65
 
66
  # 檢查回應中是否有內容
67
  if not response.candidates:
 
68
  if hasattr(response, 'prompt_feedback') and 'safety_ratings' in response.prompt_feedback:
69
  for rating in response.prompt_feedback['safety_ratings']:
70
  if rating['block_reason']:
 
91
  print(f"Error calling Gemini API: {e}")
92
  return None, f"API 呼叫失敗: {e}"
93
 
94
+ def read_google_sheet(sheet_url):
95
+ """從 Google Sheet 讀取資料。"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  try:
97
+ def build_csv_url(url: str) -> str:
98
+ parsed = urlparse(url)
99
+ path_parts = parsed.path.strip("/").split("/")
100
+ doc_id = None
101
+ if len(path_parts) >= 3 and path_parts[0] == "spreadsheets" and path_parts[1] == "d":
102
+ doc_id = path_parts[2]
103
+ qs_gid = parse_qs(parsed.query).get("gid", [None])[0]
104
+ frag_gid = None
105
+ if parsed.fragment:
106
+ frag_qs = parse_qs(parsed.fragment)
107
+ frag_gid = frag_qs.get("gid", [None])[0]
108
+ gid = qs_gid or frag_gid or "0"
109
+ if doc_id:
110
+ return f"https://docs.google.com/spreadsheets/d/{doc_id}/export?format=csv&gid={gid}"
111
+ if "/export" in parsed.path and "format=csv" in parsed.query:
112
+ return url
113
+ return url.replace("/edit#gid=0", "/export?format=csv&gid=0")
114
 
115
+ csv_url = build_csv_url(sheet_url)
116
+ df = pd.read_csv(csv_url, engine='python', on_bad_lines='warn')
117
+ return df
118
+ except Exception as e:
119
+ raise gr.Error(f"Error reading Google Sheet: {e}")
120
+
121
+ def process_sheet_data(sheet_url):
122
+ """處理試算表資料,為 Gradio DataFrame 準備。"""
123
+ try:
124
+ df = read_google_sheet(sheet_url)
125
 
126
+ if df.shape[1] < 3:
127
+ error_msg = f"Error: Google Sheet has only {df.shape[1]} columns, but 3 are expected (White Background URL, Reference Image URL, Prompt)."
128
+ raise gr.Error(error_msg)
 
 
 
 
129
 
130
+ data_list = []
131
+ for i, row in df.iterrows():
132
+ if pd.notna(row.iloc[0]) and pd.notna(row.iloc[1]) and pd.notna(row.iloc[2]):
133
+ data_list.append([i, row.iloc[0], row.iloc[1], row.iloc[2]])
134
+
135
+ return data_list
136
  except Exception as e:
137
+ raise gr.Error(f"Error processing sheet data: {e}")
138
+
139
+ def generate_image_for_row(row_index, dataframe_data):
140
+ """根據 Gradio DataFrame 的行索引生成圖片。"""
141
+ df = pd.DataFrame(dataframe_data, columns=["Index", "白背圖URL", "參考圖URL", "提示詞"])
142
+
143
+ if not (0 <= row_index < len(df)):
144
+ return None, "Error: Invalid row index."
145
+
146
+ row = df.iloc[int(row_index)]
147
+ white_background_url = row['白背圖URL']
148
+ reference_image_url = row['參考圖URL']
149
+ prompt = row['提示詞']
150
+
151
+ # 從 URL 下載圖片
152
+ wb_image = load_image_from_url(white_background_url)
153
+ ref_image = load_image_from_url(reference_image_url)
154
+
155
+ if wb_image is None or ref_image is None:
156
+ return None, "Error: One or more images failed to load from URL."
157
+
158
+ images = [wb_image, ref_image]
159
+
160
+ # 使用你提供的函式來呼叫 API
161
+ image_path, text_response = generate_content(text=prompt, images=images)
162
+
163
+ if image_path:
164
+ # 如果成功生成圖片,返回圖片路徑和操作日誌
165
+ output_dir = "generated_images"
166
+ os.makedirs(output_dir, exist_ok=True)
167
+ final_output_path = os.path.join(output_dir, os.path.basename(image_path))
168
+ os.rename(image_path, final_output_path) # 將暫存檔移動到正式目錄
169
+ return final_output_path, text_response
170
+ else:
171
+ # 如果沒有生成圖片,返回 None 和操作日誌
172
+ return None, text_response
173
+
174
+ # --- Gradio 介面設定(保留你原來的樣式) ---
175
 
 
176
  with gr.Blocks(css_paths="style.css",) as demo:
177
  gr.HTML(
178
  """
 
181
  <img src="https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png" alt="Gemini logo">
182
  </div>
183
  <div>
184
+ <h1>Gemini Image Generator</h1>
185
  <p>Powered by <a href="https://gradio.app/">Gradio</a>⚡️|
186
  <a href="https://huggingface.co/spaces/ameerazam08/Gemini-Image-Edit?duplicate=true">Duplicate</a> this Repo |
187
  <a href="https://aistudio.google.com/apikey">Get an API Key</a> |
 
202
  with gr.Accordion("📌 Usage Instructions", open=False, elem_classes="instructions-accordion"):
203
  gr.Markdown("""
204
  ### 📌 Usage
205
+ - Input a Google Sheet URL containing image URLs and a prompt.
206
+ - Select a row to generate an output.
 
 
207
  """)
208
  with gr.Row(elem_classes="main-content"):
209
  with gr.Column(elem_classes="input-column"):
210
+ sheet_url_input = gr.Textbox(label="Google Sheet URL", value="https://docs.google.com/spreadsheets/d/1G3olHxydDIbnyXdh5nnw5TG0akZFeMeYm-25JmCGDLg/edit?gid=0#gid=0")
211
+ process_button = gr.Button("處理試算表", elem_classes="generate-btn")
212
+
213
+ with gr.Row():
214
+ row_index_input = gr.Number(label="要生成的行數", precision=0, value=0)
215
+ generate_selected_button = gr.Button("生成所選行的圖片", elem_classes="generate-btn")
216
+
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  with gr.Column(elem_classes="output-column"):
218
+ output_dataframe = gr.DataFrame(
219
+ headers=["Index", "白背圖URL", "參考圖URL", "提示詞"],
220
+ col_count=(4, "fixed"),
221
+ interactive=False,
222
+ label="已處理的試算表數據"
 
223
  )
224
+ generated_image_output = gr.Image(label="生成的圖片", elem_classes="output-gallery")
225
+ operation_log_output = gr.Textbox(
226
+ label="操作日誌",
227
+ lines=10,
228
+ placeholder="文字回應和日誌會顯示在這裡。"
229
+ )
230
+
231
+ processed_df_state = gr.State()
232
+
233
+ process_button.click(
234
+ fn=process_sheet_data,
235
+ inputs=sheet_url_input,
236
+ outputs=output_dataframe
237
+ ).success(
238
+ fn=lambda x: x,
239
+ inputs=output_dataframe,
240
+ outputs=processed_df_state
241
  )
242
+
243
+ generate_selected_button.click(
244
+ fn=generate_image_for_row,
245
+ inputs=[row_index_input, processed_df_state],
246
+ outputs=[generated_image_output, operation_log_output]
 
 
 
 
 
 
 
 
 
247
  )
248
+
249
  demo.queue(max_size=50).launch(mcp_server=True, share=True)