dseditor commited on
Commit
724aa84
·
verified ·
1 Parent(s): ecfbfd1

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +14 -67
app.py CHANGED
@@ -1,20 +1,17 @@
 
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
  valid_images = [img for img in images if img is not None]
15
  if not valid_images:
16
  return []
17
-
18
  if resize_mode == "height":
19
  target_height = min(img.size[1] for img in valid_images)
20
  resized_images = []
@@ -23,7 +20,7 @@ def resize_images_to_match(images, resize_mode="height"):
23
  new_width = int(target_height * aspect_ratio)
24
  resized_img = img.resize((new_width, target_height), Image.Resampling.LANCZOS)
25
  resized_images.append(resized_img)
26
- else: # width mode
27
  target_width = min(img.size[0] for img in valid_images)
28
  resized_images = []
29
  for img in valid_images:
@@ -31,23 +28,14 @@ def resize_images_to_match(images, resize_mode="height"):
31
  new_height = int(target_width * aspect_ratio)
32
  resized_img = img.resize((target_width, new_height), Image.Resampling.LANCZOS)
33
  resized_images.append(resized_img)
34
-
35
  return resized_images
36
 
37
  def concatenate_images(images, direction="horizontal", spacing=0, background_color="white"):
38
- """
39
- 拼接圖片
40
- direction: "horizontal" - 水平拼接, "vertical" - 垂直拼接
41
- spacing: 圖片間距(像素)
42
- background_color: 背景顏色
43
- """
44
  if not images or len(images) == 0:
45
  return None
46
-
47
  valid_images = [img for img in images if img is not None]
48
  if not valid_images:
49
  return None
50
-
51
  rgb_images = []
52
  for img in valid_images:
53
  if img.mode != 'RGB':
@@ -59,10 +47,8 @@ def concatenate_images(images, direction="horizontal", spacing=0, background_col
59
  rgb_images.append(img.convert('RGB'))
60
  else:
61
  rgb_images.append(img)
62
-
63
  if len(rgb_images) == 1:
64
  return rgb_images[0]
65
-
66
  if direction == "horizontal":
67
  total_width = sum(img.size[0] for img in rgb_images) + spacing * (len(rgb_images) - 1)
68
  max_height = max(img.size[1] for img in rgb_images)
@@ -81,16 +67,11 @@ def concatenate_images(images, direction="horizontal", spacing=0, background_col
81
  x_offset = (max_width - img.size[0]) // 2
82
  result.paste(img, (x_offset, y_offset))
83
  y_offset += img.size[1] + spacing
84
-
85
  return result
86
 
87
  def save_as_jpg(image, quality=95):
88
- """
89
- 將圖片保存為高質量JPG格式
90
- """
91
  if image is None:
92
  return None
93
-
94
  if image.mode != 'RGB':
95
  if image.mode in ('RGBA', 'LA'):
96
  background = Image.new('RGB', image.size, 'white')
@@ -98,7 +79,6 @@ def save_as_jpg(image, quality=95):
98
  image = background
99
  else:
100
  image = image.convert('RGB')
101
-
102
  buffer = io.BytesIO()
103
  image.save(buffer, format='JPEG', quality=quality, optimize=True)
104
  buffer.seek(0)
@@ -108,18 +88,12 @@ def process_images(
108
  image1, image2, image3, image4, image5, image6,
109
  resize_mode, concat_direction, spacing, bg_color, jpg_quality
110
  ):
111
- """
112
- 處理圖片拼接的主函數
113
- """
114
  uploaded_images = [image1, image2, image3, image4, image5, image6]
115
  valid_images = [img for img in uploaded_images if img is not None]
116
-
117
  if len(valid_images) == 0:
118
- return None, "❌ 請至少上傳一張圖片"
119
-
120
  if len(valid_images) == 1:
121
- return valid_images[0], f"✅ 只有一張圖片,無需拼接"
122
-
123
  try:
124
  if resize_mode == "等高度 (水平拼接)":
125
  resized_images = resize_images_to_match(valid_images, "height")
@@ -127,42 +101,27 @@ def process_images(
127
  else:
128
  resized_images = resize_images_to_match(valid_images, "width")
129
  direction = "vertical"
130
-
131
  if concat_direction != "自動 (根據調整模式)":
132
  direction = "horizontal" if concat_direction == "水平拼接" else "vertical"
133
-
134
  result = concatenate_images(resized_images, direction, spacing, bg_color)
135
  if result is None:
136
- return None, "❌ 拼接失敗"
137
-
138
  result = save_as_jpg(result, quality=jpg_quality)
139
-
 
140
  info = f"✅ 成功拼接 {len(valid_images)} 張圖片\n"
141
  info += f"📐 最終尺寸: {result.size[0]} x {result.size[1]} 像素\n"
142
  info += f"🔄 調整模式: {resize_mode}\n"
143
  info += f"➡️ 拼接方向: {'水平' if direction == 'horizontal' else '垂直'}\n"
144
  info += f"📏 間距: {spacing} 像素\n"
145
  info += f"💾 輸出品質: {jpg_quality}%"
146
-
147
- return result, info
148
-
149
  except Exception as e:
150
- return None, f"❌ 處理過程中出現錯誤: {str(e)}"
151
 
152
  def create_interface():
153
  with gr.Blocks(title="圖片拼接比較工具", theme=gr.themes.Soft()) as iface:
154
- gr.Markdown("""
155
- # 🖼️ 圖片拼接比較工具
156
-
157
- 上傳多張PNG/JPG圖片,自動調整尺寸並拼接成比較圖。
158
-
159
- ### 使用說明:
160
- 1. **上傳圖片**:最多可上傳6張圖片
161
- 2. **選擇調整模式**:等高度適合水平拼接,等寬度適合垂直拼接
162
- 3. **設定參數**:調整間距和背景顏色
163
- 4. **點擊處理**:生成拼接後的比較圖
164
- """)
165
-
166
  with gr.Row():
167
  with gr.Column(scale=2):
168
  gr.Markdown("### 📁 上傳圖片")
@@ -174,7 +133,6 @@ def create_interface():
174
  image4 = gr.Image(type="pil", label="圖片 4")
175
  image5 = gr.Image(type="pil", label="圖片 5")
176
  image6 = gr.Image(type="pil", label="圖片 6")
177
-
178
  gr.Markdown("### ⚙️ 處理設定")
179
  with gr.Row():
180
  resize_mode = gr.Dropdown(
@@ -200,24 +158,13 @@ def create_interface():
200
  gr.Markdown("### 🎯 處理結果")
201
  result_image = gr.Image(type="pil", label="拼接結果")
202
  result_info = gr.Textbox(label="處理信息", lines=6, max_lines=10)
203
-
204
  process_btn.click(
205
  fn=process_images,
206
  inputs=[image1, image2, image3, image4, image5, image6,
207
  resize_mode, concat_direction, spacing, bg_color, jpg_quality],
208
- outputs=[result_image, result_info]
209
  )
210
-
211
- gr.Markdown("""
212
- ### 💡 小貼士:
213
- - **等高度模式**:所有圖片調整為相同高度,適合水平對比
214
- - **等寬度模式**:所有圖片調整為相同寬度,適合垂直對比
215
- - 支持常見圖片格式:PNG, JPG, JPEG, WEBP
216
- - 處理過程保持圖片原始寬高比
217
- - **輸出格式:JPG**
218
- - **品質建議**:95% 提供最佳品質,85% 平衡品質與檔案大小
219
- - 自動處理透明背景(轉為白色背景)
220
- """)
221
  return iface
222
 
223
  if __name__ == "__main__":
 
1
+
2
  import gradio as gr
3
  import numpy as np
4
  from PIL import Image
5
  import io
6
+ import tempfile
7
+ import os
8
 
9
  def resize_images_to_match(images, resize_mode="height"):
 
 
 
 
10
  if not images or len(images) == 0:
11
  return []
 
12
  valid_images = [img for img in images if img is not None]
13
  if not valid_images:
14
  return []
 
15
  if resize_mode == "height":
16
  target_height = min(img.size[1] for img in valid_images)
17
  resized_images = []
 
20
  new_width = int(target_height * aspect_ratio)
21
  resized_img = img.resize((new_width, target_height), Image.Resampling.LANCZOS)
22
  resized_images.append(resized_img)
23
+ else:
24
  target_width = min(img.size[0] for img in valid_images)
25
  resized_images = []
26
  for img in valid_images:
 
28
  new_height = int(target_width * aspect_ratio)
29
  resized_img = img.resize((target_width, new_height), Image.Resampling.LANCZOS)
30
  resized_images.append(resized_img)
 
31
  return resized_images
32
 
33
  def concatenate_images(images, direction="horizontal", spacing=0, background_color="white"):
 
 
 
 
 
 
34
  if not images or len(images) == 0:
35
  return None
 
36
  valid_images = [img for img in images if img is not None]
37
  if not valid_images:
38
  return None
 
39
  rgb_images = []
40
  for img in valid_images:
41
  if img.mode != 'RGB':
 
47
  rgb_images.append(img.convert('RGB'))
48
  else:
49
  rgb_images.append(img)
 
50
  if len(rgb_images) == 1:
51
  return rgb_images[0]
 
52
  if direction == "horizontal":
53
  total_width = sum(img.size[0] for img in rgb_images) + spacing * (len(rgb_images) - 1)
54
  max_height = max(img.size[1] for img in rgb_images)
 
67
  x_offset = (max_width - img.size[0]) // 2
68
  result.paste(img, (x_offset, y_offset))
69
  y_offset += img.size[1] + spacing
 
70
  return result
71
 
72
  def save_as_jpg(image, quality=95):
 
 
 
73
  if image is None:
74
  return None
 
75
  if image.mode != 'RGB':
76
  if image.mode in ('RGBA', 'LA'):
77
  background = Image.new('RGB', image.size, 'white')
 
79
  image = background
80
  else:
81
  image = image.convert('RGB')
 
82
  buffer = io.BytesIO()
83
  image.save(buffer, format='JPEG', quality=quality, optimize=True)
84
  buffer.seek(0)
 
88
  image1, image2, image3, image4, image5, image6,
89
  resize_mode, concat_direction, spacing, bg_color, jpg_quality
90
  ):
 
 
 
91
  uploaded_images = [image1, image2, image3, image4, image5, image6]
92
  valid_images = [img for img in uploaded_images if img is not None]
 
93
  if len(valid_images) == 0:
94
+ return None, "❌ 請至少上傳一張圖片", None
 
95
  if len(valid_images) == 1:
96
+ return valid_images[0], f"✅ 只有一張圖片,無需拼接", None
 
97
  try:
98
  if resize_mode == "等高度 (水平拼接)":
99
  resized_images = resize_images_to_match(valid_images, "height")
 
101
  else:
102
  resized_images = resize_images_to_match(valid_images, "width")
103
  direction = "vertical"
 
104
  if concat_direction != "自動 (根據調整模式)":
105
  direction = "horizontal" if concat_direction == "水平拼接" else "vertical"
 
106
  result = concatenate_images(resized_images, direction, spacing, bg_color)
107
  if result is None:
108
+ return None, "❌ 拼接失敗", None
 
109
  result = save_as_jpg(result, quality=jpg_quality)
110
+ temp_path = os.path.join(tempfile.gettempdir(), "result.jpg")
111
+ result.save(temp_path, format="JPEG", quality=jpg_quality)
112
  info = f"✅ 成功拼接 {len(valid_images)} 張圖片\n"
113
  info += f"📐 最終尺寸: {result.size[0]} x {result.size[1]} 像素\n"
114
  info += f"🔄 調整模式: {resize_mode}\n"
115
  info += f"➡️ 拼接方向: {'水平' if direction == 'horizontal' else '垂直'}\n"
116
  info += f"📏 間距: {spacing} 像素\n"
117
  info += f"💾 輸出品質: {jpg_quality}%"
118
+ return result, info, temp_path
 
 
119
  except Exception as e:
120
+ return None, f"❌ 處理過程中出現錯誤: {str(e)}", None
121
 
122
  def create_interface():
123
  with gr.Blocks(title="圖片拼接比較工具", theme=gr.themes.Soft()) as iface:
124
+ gr.Markdown("# 🖼️ 圖片拼接比較工具")
 
 
 
 
 
 
 
 
 
 
 
125
  with gr.Row():
126
  with gr.Column(scale=2):
127
  gr.Markdown("### 📁 上傳圖片")
 
133
  image4 = gr.Image(type="pil", label="圖片 4")
134
  image5 = gr.Image(type="pil", label="圖片 5")
135
  image6 = gr.Image(type="pil", label="圖片 6")
 
136
  gr.Markdown("### ⚙️ 處理設定")
137
  with gr.Row():
138
  resize_mode = gr.Dropdown(
 
158
  gr.Markdown("### 🎯 處理結果")
159
  result_image = gr.Image(type="pil", label="拼接結果")
160
  result_info = gr.Textbox(label="處理信息", lines=6, max_lines=10)
161
+ result_file = gr.File(label="下載 JPG 檔案")
162
  process_btn.click(
163
  fn=process_images,
164
  inputs=[image1, image2, image3, image4, image5, image6,
165
  resize_mode, concat_direction, spacing, bg_color, jpg_quality],
166
+ outputs=[result_image, result_info, result_file]
167
  )
 
 
 
 
 
 
 
 
 
 
 
168
  return iface
169
 
170
  if __name__ == "__main__":