File size: 7,889 Bytes
b79d22c
 
 
 
 
 
 
ece735c
b79d22c
 
 
 
ece735c
 
b79d22c
 
 
 
ece735c
b79d22c
 
 
 
ece735c
b79d22c
 
 
 
 
 
 
 
 
 
ece735c
b79d22c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ece735c
b79d22c
 
 
 
 
 
ece735c
b79d22c
 
ece735c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b79d22c
 
 
ece735c
 
b79d22c
ece735c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b79d22c
ece735c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b79d22c
 
ece735c
b79d22c
 
ece735c
b79d22c
ece735c
 
b79d22c
 
ece735c
 
b79d22c
ece735c
 
b79d22c
 
 
 
ece735c
b79d22c
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
import gradio as gr
from transformers import pipeline
from PIL import Image
import torch

# -----------------------------------------------------------------
# 1. 載入模型 (這會在 App 啟動時執行一次)
# (此部分與你的程式碼完全相同)
# -----------------------------------------------------------------
print("正在載入模型...")
# 模型 1: Image Captioning (圖片標註)
captioner = pipeline(
    "image-to-text",
    model="Salesforce/blip-image-captioning-base")

# 模型 2: Visual Question Answering (VQA)
vqa = pipeline(
    "visual-question-answering",
    model="dandelin/vilt-b32-finetuned-vqa")
print("模型載入完成!")

# -----------------------------------------------------------------
# 2. 定義 AI 功能的函式
# (此部分與你的程式碼完全相同)
# -----------------------------------------------------------------
def get_image_caption(pil_image):
    """
    接收一個 PIL 圖片,返回 AI 生成的文字描述。
    """
    if pil_image is None:
        return "錯誤:請先上傳一張圖片。"
    try:
        caption = captioner(pil_image)
        if caption and isinstance(caption, list) and 'generated_text' in caption[0]:
            return caption[0]['generated_text'].capitalize() # 讓首字母大寫
        else:
            return "錯誤:模型返回了非預期的格式。"
    except Exception as e:
        return f"處理時發生錯誤: {e}"

def answer_visual_question(pil_image, question_text):
    """
    接收一個 PIL 圖片和一個問題字串,返回 AI 的回答。
    """
    if pil_image is None:
        return "錯誤:請先上傳一張圖片。"
    if not question_text:
        return "錯誤:請輸入你的問題。"
    try:
        answer = vqa(image=pil_image, question=question_text)
        if answer and isinstance(answer, list) and 'answer' in answer[0]:
            return answer[0]['answer'].capitalize() # 讓首字母大寫
        else:
            return "錯誤:模型返回了非預期的格式。"
    except Exception as e:
        return f"處理時發生錯誤: {e}"

# -----------------------------------------------------------------
# 3. (全新設計) 建立 Gradio Web UI
# -----------------------------------------------------------------

# (新增) 自訂 CSS 樣式
# 我們將使用這些 CSS 來打造卡片佈局和美化元件
my_custom_css = """
/* --- 1. 整體頁面背景 --- */
.gradio-container {
    background: linear-gradient(to right, #e0eafc, #cfdef3); /* 淡雅的漸層背景 */
    padding: 20px;
}

/* --- 2. 美化主標題 --- */
#main-title {
    text-align: center;
    color: #2c3e50;
    font-size: 3em;
    text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
    margin-bottom: 20px;
}

/* --- 3. 卡片式佈局 (gr.Group) --- */
.gradio-group {
    background: rgba(255, 255, 255, 0.9); /* 半透明白色背景 */
    border-radius: 20px;
    box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15); /* 更柔和的陰影 */
    border: 1px solid rgba(255, 255, 255, 0.18);
    padding: 25px !important;
    margin-bottom: 20px !important;
}

/* --- 4. 美化功能標題 (h3) --- */
.gradio-group h3 {
    color: #34495e;
    font-size: 1.5em;
    border-bottom: 2px solid #4a90e2; /* 標題底線 */
    padding-bottom: 5px;
}

/* --- 5. 美化主要按鈕 --- */
.gr-button-primary {
    background: linear-gradient(to right, #4a90e2, #50E3C2) !important;
    border: none !important;
    border-radius: 10px !important;
    font-size: 1.1em !important;
    color: white !important;
    padding: 12px 24px !important;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1) !important;
    transition: all 0.3s ease !important;
}
.gr-button-primary:hover {
    transform: translateY(-2px) !important;
    box-shadow: 0 6px 12px rgba(0,0,0,0.15) !important;
}

/* --- 6. 美化輸出文字框 (使其看起來像"顯示面板") --- */
#output-caption textarea, #output-vqa textarea {
    background: #f4f7f6 !important;
    border: 2px solid #bdc3c7 !important;
    border-radius: 10px !important;
    font-size: 1.15em !important;
    color: #2c3e50 !important;
    font-weight: 500 !important;
}
"""

with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), css=my_custom_css) as demo:

    # App 標題 (使用 elem_id 以便 CSS 選取)
    gr.Markdown(
        """
        # 🎨 視覺語言 AI 展示 (Vision Language AI Demo)
        """,
        elem_id="main-title"
    )
    gr.Markdown("### 統一上傳圖片,然後在下方體驗兩種不同的 AI 功能。")

    with gr.Row(variant="panel"):
        # --- (重新設計) 佈局:左側為輸入 ---
        with gr.Column(scale=2):
            with gr.Group():
                gr.Markdown("### 1. 上傳圖片 (Input)")
                # 建立一個共用的圖片輸入元件
                shared_image_input = gr.Image(type="pil", label="上傳圖片")
                
                gr.Markdown("#### 試試看範例圖片:")
                # 範例功能 (會自動填入上面的 shared_image_input)
                gr.Examples(
                    examples=[
                        ["sample_images/cat.jpg"],
                        ["sample_images/dog.jpg"]
                    ],
                    inputs=shared_image_input,
                    label="點選範例圖片"
                )

        # --- (重新設計) 佈局:右側為功能輸出 ---
        with gr.Column(scale=3):
            # --- 功能一:圖片標註 ---
            with gr.Group():
                gr.Markdown("### 2. 功能一:圖片標註 (Image Captioning)")
                caption_button = gr.Button("生成圖片描述", variant="primary")
                caption_output_text = gr.Textbox(
                    label="AI 生成的描述", 
                    interactive=False, 
                    elem_id="output-caption" # 綁定 CSS ID
                )
            
            # --- 功能二:視覺問答 (VQA) ---
            with gr.Group():
                gr.Markdown("### 3. 功能二:視覺問答 (VQA)")
                vqa_input_question = gr.Textbox(
                    label="你的問題", 
                    placeholder="例如:圖片中有幾個人? (What color is the car?)"
                )
                vqa_button = gr.Button("對圖片提出問題", variant="primary")
                vqa_output_answer = gr.Textbox(
                    label="AI 的回答", 
                    interactive=False, 
                    elem_id="output-vqa" # 綁定 CSS ID
                )
                
                # VQA 的範例 (會同時填入圖片 和 問題)
                gr.Examples(
                    examples=[
                        ["sample_images/cat.jpg", "What color is the cat?"],
                        ["sample_images/dog.jpg", "What color is the dog?"]
                    ],
                    inputs=[shared_image_input, vqa_input_question],
                    label="點選範例 (包含圖片與問題)"
                )

    # -----------------------------------------------------------------
    # 4. 綁定按鈕和函式 (邏輯不變,只是元件名稱更新)
    # -----------------------------------------------------------------
    
    # 功能一:
    caption_button.click(
        fn=get_image_caption,
        inputs=[shared_image_input],  # 輸入來自共用的圖片
        outputs=[caption_output_text]
    )

    # 功能二:
    vqa_button.click(
        fn=answer_visual_question,
        inputs=[shared_image_input, vqa_input_question], # 輸入來自共用圖片 + VQA問題
        outputs=[vqa_output_answer]
    )

# -----------------------------------------------------------------
# 5. 啟動 App (此部分與你的程式碼相同)
# -----------------------------------------------------------------
if __name__ == "__main__":
    demo.launch(debug=True)