Hank20041016 commited on
Commit
1318091
·
verified ·
1 Parent(s): d150f44

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +46 -319
app.py CHANGED
@@ -1,328 +1,55 @@
1
- import os
2
- import gc
3
- import torch
4
  from transformers import pipeline
5
  import gradio as gr
6
  from PIL import Image
7
  import requests
8
  from io import BytesIO
9
- import psutil
10
- from datetime import datetime
11
 
12
- # 設定環境變數,使用臨時目錄避免快速填滿存儲
13
- os.environ["TRANSFORMERS_CACHE"] = "/tmp/transformers_cache"
14
- os.environ["HF_HOME"] = "/tmp/hf_home"
15
- os.environ["TORCH_HOME"] = "/tmp/torch_cache"
16
-
17
- def clear_memory():
18
- """清理記憶體和快取"""
19
- gc.collect()
20
- if torch.cuda.is_available():
21
- torch.cuda.empty_cache()
22
-
23
- def check_storage():
24
- """檢查存儲空間"""
25
- try:
26
- disk_usage = psutil.disk_usage('/')
27
- free_gb = disk_usage.free / (1024**3)
28
- used_percent = (disk_usage.used / disk_usage.total) * 100
29
- return free_gb, used_percent
30
- except:
31
- return 0, 100
32
-
33
- def load_medgemma_model():
34
- """載入 MedGemma 模型,使用優化設定"""
35
- try:
36
- print("🏥 正在載入 MedGemma-4B 模型...")
37
- print(f"⏰ 載入時間: {datetime.now().strftime('%H:%M:%S')}")
38
-
39
- # 檢查存儲空間
40
- free_gb, used_percent = check_storage()
41
- print(f"💾 可用空間: {free_gb:.1f}GB, 使用率: {used_percent:.1f}%")
42
-
43
- if free_gb < 5: # 如果可用空間少於5GB
44
- raise Exception(f"存儲空間不足 ({free_gb:.1f}GB),建議至少需要 5GB")
45
-
46
- # 使用優化設定載入模型
47
- pipe = pipeline(
48
- "image-to-text",
49
- model="google/medgemma-4b-it",
50
- torch_dtype=torch.float16, # 使用半精度節省記憶體
51
- device_map="auto",
52
- low_cpu_mem_usage=True,
53
- cache_dir="/tmp/transformers_cache"
54
- )
55
-
56
- print("✅ MedGemma-4B 模型載入成功!")
57
- return pipe, "google/medgemma-4b-it"
58
-
59
- except Exception as e:
60
- print(f"❌ MedGemma 載入失敗: {e}")
61
- print("🔄 嘗試載入較小的替代模型...")
62
-
63
- try:
64
- # 載入較小的醫療相關模型作為替代
65
- pipe = pipeline(
66
- "image-to-text",
67
- model="Salesforce/blip-image-captioning-base",
68
- cache_dir="/tmp/transformers_cache"
69
- )
70
- print("✅ 已載入 BLIP 模型作為替代")
71
- return pipe, "Salesforce/blip-image-captioning-base"
72
- except Exception as e2:
73
- raise Exception(f"所有模型載入失敗: MedGemma({e}), BLIP({e2})")
74
 
 
75
  def load_image_from_input(image_input):
76
- """處理圖片輸入:PIL Image、檔案路徑或 URL"""
77
- try:
78
- # JPG 檔案上傳(Gradio 返回 PIL Image)
79
- if isinstance(image_input, Image.Image):
80
- return image_input
81
-
82
- # URL 輸入
83
- elif isinstance(image_input, str):
84
- if image_input.startswith(("http://", "https://")):
85
- print(f"📥 正在下載圖片: {image_input[:50]}...")
86
- response = requests.get(image_input, timeout=10)
87
- response.raise_for_status()
88
- image = Image.open(BytesIO(response.content))
89
- print("✅ 圖片下載成功")
90
- return image
91
- else:
92
- # 檔案路徑
93
- return Image.open(image_input)
94
- else:
95
- return Image.open(image_input)
96
-
97
- except Exception as e:
98
- raise Exception(f"無法載入圖片: {e}")
99
-
100
- def predict(image_input, question, url_input):
101
- """主要預測函數"""
102
- try:
103
- # 確定圖片來源(優先使用上傳的圖片)
104
- if image_input is not None:
105
- image_source = image_input
106
- source_type = "上傳檔案"
107
- elif url_input and url_input.strip():
108
- image_source = url_input.strip()
109
- source_type = "URL"
110
- else:
111
- return "❌ 請上傳圖片或輸入圖片 URL"
112
-
113
- print(f"📷 處理圖片來源: {source_type}")
114
-
115
- # 載入圖片
116
- image = load_image_from_input(image_source)
117
-
118
- # 圖片預處理
119
- original_size = image.size
120
- if image.mode != 'RGB':
121
- image = image.convert('RGB')
122
- print(f"🔄 轉換圖片格式: {image.mode}")
123
-
124
- # 調整圖片大小以節省記憶體(保持品質)
125
- max_size = 768 # MedGemma 建議大小
126
- if max(image.size) > max_size:
127
- ratio = max_size / max(image.size)
128
- new_size = tuple(int(dim * ratio) for dim in image.size)
129
- image = image.resize(new_size, Image.Resampling.LANCZOS)
130
- print(f"📐 調整圖片大小: {original_size} → {image.size}")
131
-
132
- # 處理問題輸入
133
- if not question or not question.strip():
134
- question = "請詳細分析這張醫療影像,描述你看��的重要特徵、可能的病理變化,以及任何需要注意的異常。"
135
-
136
- question = question.strip()
137
- print(f"❓ 醫療問題: {question[:100]}...")
138
-
139
- # 根據模型類型選擇輸入格式
140
- global model_name
141
- if "medgemma" in model_name.lower():
142
- # MedGemma 使用對話格式
143
- messages = [
144
- {
145
- "role": "user",
146
- "content": [
147
- {"type": "image", "image": image},
148
- {"type": "text", "text": question}
149
- ]
150
- }
151
- ]
152
- print("🔬 使用 MedGemma 專業醫療分析模式")
153
- result = pipe(messages)
154
- else:
155
- # 其他模型直接使用圖片
156
- print("🔍 使用通用圖片描述模式")
157
- result = pipe(image)
158
-
159
- # 清理記憶體
160
- clear_memory()
161
-
162
- # 解析結果
163
- if isinstance(result, list) and len(result) > 0:
164
- if isinstance(result[0], dict):
165
- generated_text = result[0].get('generated_text', str(result[0]))
166
- else:
167
- generated_text = str(result[0])
168
- else:
169
- generated_text = str(result)
170
-
171
- # 添加分析資訊
172
- analysis_info = f"""
173
- 🏥 **醫療影像分析結果**
174
-
175
- 📊 **圖片資訊:**
176
- - 原始尺寸: {original_size}
177
- - 處理尺寸: {image.size}
178
- - 來源: {source_type}
179
-
180
- 🤖 **使用模型:** {model_name}
181
-
182
- 🔬 **分析結果:**
183
- {generated_text}
184
-
185
- ---
186
- ⚠️ **重要提醒:** 此分析僅供參考,不能替代專業醫療診斷。如有疑慮請諮詢專業醫師。
187
- """
188
-
189
- return analysis_info
190
-
191
- except Exception as e:
192
- clear_memory()
193
- error_msg = f"❌ 處理錯誤: {str(e)}"
194
- print(error_msg)
195
- return error_msg
196
-
197
- # 載入模型
198
- try:
199
- pipe, model_name = load_medgemma_model()
200
- model_status = f"✅ {model_name} 已準備就緒"
201
- except Exception as e:
202
- model_status = f"❌ 模型載入失敗: {e}"
203
- pipe = None
204
- model_name = "未載入"
205
-
206
- # 創建 Gradio 介面
207
- def create_interface():
208
- with gr.Blocks(
209
- title="MedGemma 醫療影像分析系統",
210
- theme=gr.themes.Soft(),
211
- css=".gradio-container {max-width: 1200px; margin: auto;}"
212
- ) as demo:
213
-
214
- gr.Markdown(f"""
215
- # 🏥 MedGemma 醫療影像分析系統
216
-
217
- **模型狀態:** {model_status}
218
- **更新時間:** {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
219
-
220
- 上傳醫療影像(JPG/PNG)或輸入圖片 URL,獲得專業的 AI 醫療影像分析。
221
- """)
222
-
223
- with gr.Row():
224
- with gr.Column(scale=1):
225
- # 圖片上傳
226
- image_input = gr.Image(
227
- label="📤 上傳醫療影像",
228
- type="pil",
229
- file_types=["jpg", "jpeg", "png"],
230
- height=300
231
- )
232
-
233
- # URL 輸入
234
- url_input = gr.Textbox(
235
- label="🔗 或輸入圖片 URL",
236
- placeholder="https://example.com/medical-image.jpg",
237
- lines=1
238
- )
239
-
240
- # 問題輸入
241
- question_input = gr.Textbox(
242
- label="❓ 醫療問題或分析要求",
243
- placeholder="請分析這張X光片中的異常...",
244
- lines=3,
245
- value="請詳細分析這張醫療影像,包括任何可見的異常或重要特徵。"
246
- )
247
-
248
- # 分析按鈕
249
- analyze_btn = gr.Button(
250
- "🔬 開始分析",
251
- variant="primary",
252
- size="lg"
253
- )
254
-
255
- # 清理按鈕
256
- clear_btn = gr.Button("🧹 清理", variant="secondary")
257
-
258
- with gr.Column(scale=2):
259
- # 分析結果
260
- output = gr.Textbox(
261
- label="📋 分析結果",
262
- lines=20,
263
- interactive=False,
264
- show_copy_button=True
265
- )
266
-
267
- # 使用說明
268
- with gr.Accordion("📖 使用說明", open=False):
269
- gr.Markdown("""
270
- ### 如何使用:
271
- 1. **上傳圖片**: 點擊上傳區域選擇 JPG/PNG 醫療影像
272
- 2. **或使用 URL**: 在 URL 欄位貼上圖片連結
273
- 3. **輸入問題**: 描述你想了解的醫療問題
274
- 4. **開始分析**: 點擊分析按鈕獲得結果
275
-
276
- ### 支援的影像類型:
277
- - X光片 (X-ray)
278
- - CT 掃描 (CT Scan)
279
- - MRI 影像 (MRI)
280
- - 超音波影像 (Ultrasound)
281
- - 病理切片 (Pathology)
282
-
283
- ### 重要提醒:
284
- ⚠️ 此 AI 分析僅供參考學習,不可作為醫療診斷依據
285
- ⚠️ 如有健康疑慮,請務必諮詢專業醫師
286
- """)
287
-
288
- # 事件綁定
289
- analyze_btn.click(
290
- fn=predict,
291
- inputs=[image_input, question_input, url_input],
292
- outputs=output
293
- )
294
-
295
- clear_btn.click(
296
- fn=lambda: ("", "", ""),
297
- outputs=[image_input, url_input, output]
298
- )
299
-
300
- # 圖片上傳時自動分析
301
- image_input.change(
302
- fn=lambda img, q, url: predict(img, q, url) if img is not None else "",
303
- inputs=[image_input, question_input, url_input],
304
- outputs=output
305
- )
306
-
307
- return demo
308
-
309
- # 啟動應用
310
- if __name__ == "__main__":
311
- if pipe is None:
312
- print("❌ 無法啟動:模型載入失敗")
313
- exit(1)
314
-
315
- print("🚀 啟動 MedGemma 醫療影像分析系統...")
316
-
317
- # 檢查最終狀態
318
- free_gb, used_percent = check_storage()
319
- print(f"💾 當前存儲狀態: {free_gb:.1f}GB 可用, {used_percent:.1f}% 已使用")
320
 
321
- demo = create_interface()
322
- demo.launch(
323
- server_name="0.0.0.0",
324
- server_port=7860,
325
- debug=False,
326
- show_error=True,
327
- share=False
328
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from transformers import pipeline
2
  import gradio as gr
3
  from PIL import Image
4
  import requests
5
  from io import BytesIO
 
 
6
 
7
+ # 建立 pipeline
8
+ pipe = pipeline("image-to-text", model="google/medgemma-4b-it")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ # 修正:支援 JPG 檔案上傳
11
  def load_image_from_input(image_input):
12
+ # URL 情況
13
+ if isinstance(image_input, str) and (image_input.startswith("http://") or image_input.startswith("https://")):
14
+ try:
15
+ response = requests.get(image_input)
16
+ img = Image.open(BytesIO(response.content))
17
+ return img
18
+ except Exception as e:
19
+ raise gr.Error(f"無法從 URL 下載圖片: {e}")
20
+ else:
21
+ # JPG 檔案上傳情況 - 這裡就是關鍵修正
22
+ return Image.open(image_input)
23
+
24
+ # 包裝成 API 函數
25
+ def predict(image_input, question):
26
+ image = load_image_from_input(image_input)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ # 將輸入轉換為模型所需的 messages 格式
29
+ messages = [
30
+ {
31
+ "role": "user",
32
+ "content": [
33
+ {"type": "image", "image": image}, # 修正:改為 "image"
34
+ {"type": "text", "text": question}
35
+ ]
36
+ },
37
+ ]
38
+ result = pipe(messages)
39
+ return result[0]["generated_text"]
40
+
41
+ # Gradio 介面
42
+ iface = gr.Interface(
43
+ fn=predict,
44
+ inputs=[
45
+ gr.Image(type="filepath", file_types=[".jpg", ".jpeg", ".png"]), # 修正:加上檔案類型限制
46
+ "text"
47
+ ],
48
+ outputs="text",
49
+ title="MedGemma API + Demo",
50
+ description="上傳 JPG 圖片或輸入圖片 URL,以 API 或 UI 測試 MedGemma。"
51
+ )
52
+
53
+ # 啟動應用程式
54
+ if __name__ == "__main__": # 修正:語法錯誤
55
+ iface.launch()