dseditor commited on
Commit
5f6ef81
·
verified ·
1 Parent(s): e39b305

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +238 -0
  2. requirements.txt +3 -0
app.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ from PIL import Image
4
+ import io
5
+
6
+ def resize_images_to_match(images, resize_mode="height"):
7
+ """
8
+ 調整圖片尺寸以匹配拼接需求
9
+ resize_mode: "height" - 垂直邊等長(水平拼接), "width" - 水平邊等長(垂直拼接)
10
+ """
11
+ if not images or len(images) == 0:
12
+ return []
13
+
14
+ # 過濾掉None值
15
+ valid_images = [img for img in images if img is not None]
16
+ if not valid_images:
17
+ return []
18
+
19
+ if resize_mode == "height":
20
+ # 找到最小高度,保持寬高比調整
21
+ target_height = min(img.size[1] for img in valid_images)
22
+ resized_images = []
23
+
24
+ for img in valid_images:
25
+ # 計算新寬度以保持寬高比
26
+ aspect_ratio = img.size[0] / img.size[1]
27
+ new_width = int(target_height * aspect_ratio)
28
+ resized_img = img.resize((new_width, target_height), Image.Resampling.LANCZOS)
29
+ resized_images.append(resized_img)
30
+
31
+ else: # width mode
32
+ # 找到最小寬度,保持寬高比調整
33
+ target_width = min(img.size[0] for img in valid_images)
34
+ resized_images = []
35
+
36
+ for img in valid_images:
37
+ # 計算新高度以保持寬高比
38
+ aspect_ratio = img.size[1] / img.size[0]
39
+ new_height = int(target_width * aspect_ratio)
40
+ resized_img = img.resize((target_width, new_height), Image.Resampling.LANCZOS)
41
+ resized_images.append(resized_img)
42
+
43
+ return resized_images
44
+
45
+ def concatenate_images(images, direction="horizontal", spacing=0, background_color="white"):
46
+ """
47
+ 拼接圖片
48
+ direction: "horizontal" - 水平拼接, "vertical" - 垂直拼接
49
+ spacing: 圖片間距(像素)
50
+ background_color: 背景顏色
51
+ """
52
+ if not images or len(images) == 0:
53
+ return None
54
+
55
+ # 過濾掉None值
56
+ valid_images = [img for img in images if img is not None]
57
+ if not valid_images:
58
+ return None
59
+
60
+ if len(valid_images) == 1:
61
+ return valid_images[0]
62
+
63
+ if direction == "horizontal":
64
+ # 水平拼接
65
+ total_width = sum(img.size[0] for img in valid_images) + spacing * (len(valid_images) - 1)
66
+ max_height = max(img.size[1] for img in valid_images)
67
+
68
+ # 創建新圖片
69
+ result = Image.new('RGB', (total_width, max_height), background_color)
70
+
71
+ x_offset = 0
72
+ for img in valid_images:
73
+ # 垂直居中放置
74
+ y_offset = (max_height - img.size[1]) // 2
75
+ result.paste(img, (x_offset, y_offset))
76
+ x_offset += img.size[0] + spacing
77
+
78
+ else: # vertical
79
+ # 垂直拼接
80
+ max_width = max(img.size[0] for img in valid_images)
81
+ total_height = sum(img.size[1] for img in valid_images) + spacing * (len(valid_images) - 1)
82
+
83
+ # 創建新圖片
84
+ result = Image.new('RGB', (max_width, total_height), background_color)
85
+
86
+ y_offset = 0
87
+ for img in valid_images:
88
+ # 水平居中放置
89
+ x_offset = (max_width - img.size[0]) // 2
90
+ result.paste(img, (x_offset, y_offset))
91
+ y_offset += img.size[1] + spacing
92
+
93
+ return result
94
+
95
+ def process_images(image1, image2, image3, image4, image5, image6,
96
+ resize_mode, concat_direction, spacing, bg_color):
97
+ """
98
+ 處理圖片拼接的主函數
99
+ """
100
+ # 收集所有上傳的圖片
101
+ uploaded_images = [image1, image2, image3, image4, image5, image6]
102
+
103
+ # 過濾掉None值
104
+ valid_images = [img for img in uploaded_images if img is not None]
105
+
106
+ if len(valid_images) == 0:
107
+ return None, "❌ 請至少上傳一張圖片"
108
+
109
+ if len(valid_images) == 1:
110
+ return valid_images[0], f"✅ 只有一張圖片,無需拼接"
111
+
112
+ try:
113
+ # 調整圖片尺寸
114
+ if resize_mode == "等高度 (水平拼接)":
115
+ resized_images = resize_images_to_match(valid_images, "height")
116
+ direction = "horizontal"
117
+ else: # 等寬度 (垂直拼接)
118
+ resized_images = resize_images_to_match(valid_images, "width")
119
+ direction = "vertical"
120
+
121
+ # 如果用戶選擇了不同的拼接方向,使用用戶選擇
122
+ if concat_direction != "自動 (根據調整模式)":
123
+ direction = "horizontal" if concat_direction == "水平拼接" else "vertical"
124
+
125
+ # 拼接圖片
126
+ result = concatenate_images(resized_images, direction, spacing, bg_color)
127
+
128
+ if result is None:
129
+ return None, "❌ 拼接失败"
130
+
131
+ # 返回結果和統計信息
132
+ info = f"✅ 成功拼接 {len(valid_images)} 張圖片\n"
133
+ info += f"📐 最終尺寸: {result.size[0]} x {result.size[1]} 像素\n"
134
+ info += f"🔄 調整模式: {resize_mode}\n"
135
+ info += f"➡️ 拼接方向: {'水平' if direction == 'horizontal' else '垂直'}\n"
136
+ info += f"📏 間距: {spacing} 像素"
137
+
138
+ return result, info
139
+
140
+ except Exception as e:
141
+ return None, f"❌ 處理過程中出現錯誤: {str(e)}"
142
+
143
+ # 創建Gradio界面
144
+ def create_interface():
145
+ with gr.Blocks(title="圖片拼接比較工具", theme=gr.themes.Soft()) as iface:
146
+ gr.Markdown("""
147
+ # 🖼️ 圖片拼接比較工具
148
+
149
+ 上傳多張PNG/JPG圖片,自動調整尺寸並拼接成比較圖。
150
+
151
+ ### 使用說明:
152
+ 1. **上傳圖片**:最多可上傳6張圖片
153
+ 2. **選擇調整模式**:等高度適合水平拼接,等寬度適合垂直拼接
154
+ 3. **設定參數**:調整間距和背景顏色
155
+ 4. **點擊處理**:生成拼接後的比較圖
156
+ """)
157
+
158
+ with gr.Row():
159
+ with gr.Column(scale=2):
160
+ gr.Markdown("### 📁 上傳圖片")
161
+
162
+ # 圖片上傳組件
163
+ with gr.Row():
164
+ image1 = gr.Image(type="pil", label="圖片 1")
165
+ image2 = gr.Image(type="pil", label="圖片 2")
166
+ image3 = gr.Image(type="pil", label="圖片 3")
167
+
168
+ with gr.Row():
169
+ image4 = gr.Image(type="pil", label="圖片 4")
170
+ image5 = gr.Image(type="pil", label="圖片 5")
171
+ image6 = gr.Image(type="pil", label="圖片 6")
172
+
173
+ gr.Markdown("### ⚙️ 處理設定")
174
+
175
+ with gr.Row():
176
+ resize_mode = gr.Dropdown(
177
+ choices=["等高度 (水平拼接)", "等寬度 (垂直拼接)"],
178
+ value="等高度 (水平拼接)",
179
+ label="尺寸調整模式"
180
+ )
181
+
182
+ concat_direction = gr.Dropdown(
183
+ choices=["自動 (根據調整模式)", "水平拼接", "垂直拼接"],
184
+ value="自動 (根據調整模式)",
185
+ label="拼接方向"
186
+ )
187
+
188
+ with gr.Row():
189
+ spacing = gr.Slider(
190
+ minimum=0,
191
+ maximum=50,
192
+ value=2,
193
+ step=1,
194
+ label="圖片間距 (像素)"
195
+ )
196
+
197
+ bg_color = gr.Dropdown(
198
+ choices=["white", "black", "gray", "transparent"],
199
+ value="white",
200
+ label="背景顏色"
201
+ )
202
+
203
+ process_btn = gr.Button("🚀 開始處理", variant="primary", size="lg")
204
+
205
+ with gr.Column(scale=2):
206
+ gr.Markdown("### 🎯 處理結果")
207
+ result_image = gr.Image(type="pil", label="拼接結果")
208
+ result_info = gr.Textbox(label="處理信息", lines=6, max_lines=10)
209
+
210
+ # 綁定處理函數
211
+ process_btn.click(
212
+ fn=process_images,
213
+ inputs=[
214
+ image1, image2, image3, image4, image5, image6,
215
+ resize_mode, concat_direction, spacing, bg_color
216
+ ],
217
+ outputs=[result_image, result_info]
218
+ )
219
+
220
+ gr.Markdown("""
221
+ ### 💡 小貼士:
222
+ - **等高度模式**:所有圖片調整為相同高度,適合水平對比
223
+ - **等寬度模式**:所有圖片調整為相同寬度,適合垂直對比
224
+ - 支持常見圖片格式:PNG, JPG, JPEG, WEBP
225
+ - 處理過程保持圖片原始寬高比
226
+ - 可下載處理後的結果圖片
227
+ """)
228
+
229
+ return iface
230
+
231
+ # 啟動應用
232
+ if __name__ == "__main__":
233
+ app = create_interface()
234
+ app.launch(
235
+ server_name="0.0.0.0",
236
+ server_port=7860,
237
+ share=True
238
+ )
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio
2
+ Pillow
3
+ numpy