PSNbst commited on
Commit
14c46f5
·
verified ·
1 Parent(s): 2db7422

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +133 -79
app.py CHANGED
@@ -1,97 +1,151 @@
1
  import gradio as gr
 
2
  import io
3
  import zipfile
4
 
5
- # 自定义CSS,固定左右面板的大小 & 按钮样式
6
- CUSTOM_CSS = """
7
- /* 左侧面板 */
8
- #left-panel {
9
- width: 300px;
10
- height: 400px; /* 固定高度,可根据需要调节 */
11
- overflow-y: auto; /* 超出时滚动 */
12
- border-right: 1px solid #ccc;
13
- padding: 10px;
14
- box-sizing: border-box;
15
- }
16
-
17
- /* 右侧面板 */
18
- #right-panel {
19
- width: 300px;
20
- height: 400px; /* 固定高度 */
21
- overflow-y: auto;
22
- border-left: 1px solid #ccc;
23
- padding: 10px;
24
- box-sizing: border-box;
25
- }
26
-
27
- /* 合并按钮 */
28
- #combine-button {
29
- background-color: orange;
30
- color: white;
31
- font-size: 16px;
32
- border-radius: 5px;
33
- border: none;
34
- padding: 10px 20px;
35
- cursor: pointer;
36
- }
37
 
38
- /* 下载ZIP按钮 */
39
- #download-zip {
40
- background-color: blue;
41
- color: white;
42
- font-size: 16px;
43
- border-radius: 5px;
44
- border: none;
45
- padding: 10px 20px;
46
- cursor: pointer;
47
- }
48
- """
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- def combine_action():
51
  """
52
- 合并按钮点击后,你可以在这里执行实际逻辑。
53
- 下面仅示例返回一个字符串或做一个简单处理。
 
 
 
54
  """
55
- print("合并逻辑被触发!")
56
- return "已合并(此处可替换为实际处理逻辑)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
- def create_zip():
59
  """
60
- 动态生成并返回一个 zip 文件(内存中)。
61
- Gradio 会将返回的 BytesIO 作为 File 输出,
62
- 用户看到下载链接后可点击保存。
63
  """
 
 
 
 
 
 
 
 
64
  buf = io.BytesIO()
65
- with zipfile.ZipFile(buf, 'w') as zf:
66
- # zip 中写一个示例文件
67
- zf.writestr("test.txt", "这是一个测试文件!\nHello World!")
 
 
 
 
68
  buf.seek(0)
69
- return buf # 返回内存中的 zip
70
 
71
- # 搭建Gradio界面
72
- with gr.Blocks(css=CUSTOM_CSS) as demo:
73
- # 用 gr.Row() + gr.Column() 实现左右并排
74
- with gr.Row():
75
- with gr.Column(elem_id="left-panel"):
76
- gr.Markdown("**上传区域**")
77
- uploader = gr.File(label="在此处上传文件")
78
-
79
- with gr.Column(elem_id="right-panel"):
80
- gr.Markdown("**结果缩略图**")
81
- gr.Markdown("这里可以放置处理后的缩略图或任何输出")
82
 
83
- # 底部按钮(同一行)
 
 
 
 
 
 
 
 
84
  with gr.Row():
85
- combine_btn = gr.Button("合并", elem_id="combine-button")
86
- download_btn = gr.Button("下载 Zip", elem_id="download-zip")
87
-
88
- # 点击“合并”时(这里仅做一个演示)
89
- combine_btn.click(fn=combine_action, inputs=[], outputs=[])
90
-
91
- # 用一个隐藏的 File 组件承载生成的 zip 文件
92
- zip_file = gr.File(label="点此下载生成的 ZIP", visible=False)
93
-
94
- # 点击“下载 Zip”时,调用 create_zip,输出到 zip_file
95
- download_btn.click(fn=create_zip, inputs=[], outputs=zip_file)
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  demo.launch()
 
1
  import gradio as gr
2
+ from PIL import Image
3
  import io
4
  import zipfile
5
 
6
+ def resize_image_to_multiple_of_64(img: Image.Image, fill_color=(0, 0, 0)):
7
+ """
8
+ 将单张图片缩放到 (w', h'),其中 w'、h' 均为 64 的整数倍。
9
+ 若原图宽高比与 w'/h' 不完全一致,则在空余处用 fill_color 填充(不裁切)。
10
+ """
11
+ w, h = img.size
12
+
13
+ # 计算最接近的 64 倍数
14
+ # 若 round(...) 结果为 0,至少保底 64
15
+ w64 = max(64, round(w / 64) * 64)
16
+ h64 = max(64, round(h / 64) * 64)
17
+
18
+ # 根据最小缩放策略,保证图片能放进 w64 × h64
19
+ scale = min(w64 / w, h64 / h)
20
+ new_width = int(w * scale)
21
+ new_height = int(h * scale)
22
+
23
+ # 创建背景
24
+ new_img = Image.new("RGB", (w64, h64), fill_color)
25
+ # 将原图缩放
26
+ scaled_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
27
+ # 居中贴到背景上
28
+ offset_x = (w64 - new_width) // 2
29
+ offset_y = (h64 - new_height) // 2
30
+ new_img.paste(scaled_img, (offset_x, offset_y))
31
+ return new_img
 
 
 
 
 
 
32
 
33
+ def make_collage(four_images, fill_color=(0, 0, 0)):
34
+ """
35
+ 4 张同样尺寸的图片(2×2)拼接成一张大图。
36
+ 如果结果大于 2048×2048,则等比例缩小到不超过 2048。
37
+ """
38
+ assert len(four_images) == 4, "必须是 4 张图"
39
+ w, h = four_images[0].size # 假设 4 张图尺寸相同
40
+ collage = Image.new("RGB", (2 * w, 2 * h), fill_color)
41
+ # 依次贴上
42
+ collage.paste(four_images[0], (0, 0))
43
+ collage.paste(four_images[1], (w, 0))
44
+ collage.paste(four_images[2], (0, h))
45
+ collage.paste(four_images[3], (w, h))
46
+
47
+ cw, ch = collage.size
48
+ if cw > 2048 or ch > 2048:
49
+ scale = min(2048 / cw, 2048 / ch)
50
+ new_cw = int(cw * scale)
51
+ new_ch = int(ch * scale)
52
+ collage = collage.resize((new_cw, new_ch), Image.Resampling.LANCZOS)
53
+
54
+ return collage
55
 
56
+ def process_images(uploaded_files):
57
  """
58
+ 处理用户上传的多张图片:
59
+ 1. 每组 4 张图 -> 统一到 64 的倍数尺寸 -> 拼成 2×2 的大图
60
+ 2. 大于 2048×2048 则缩小
61
+ 3. 返回多张拼接图(以列表的形式)供 Gradio 显示
62
+ 4. 若最后一组不足 4 张,则跳过
63
  """
64
+ # 读取成 PIL 对象
65
+ pil_images = []
66
+ for file_obj in uploaded_files:
67
+ if file_obj is not None:
68
+ img = Image.open(file_obj.name).convert("RGB")
69
+ pil_images.append(img)
70
+
71
+ results = []
72
+ # 以 4 张为一组进行处理
73
+ for i in range(0, len(pil_images), 4):
74
+ group = pil_images[i : i + 4]
75
+ if len(group) < 4:
76
+ # 不足 4 张,跳过
77
+ break
78
+
79
+ # 分别 resize
80
+ resized = [resize_image_to_multiple_of_64(img, fill_color=(0,0,0)) for img in group]
81
+ # 拼接
82
+ collage = make_collage(resized, fill_color=(0,0,0))
83
+ results.append(collage)
84
+
85
+ return results
86
 
87
+ def process_and_zip(uploaded_files):
88
  """
89
+ 在内存中创建 Zip,将所有生成的拼接图打包。
90
+ 返回一个 BytesIO Gradio 的 File 输出,用于下载。
 
91
  """
92
+ # 先按需求生成拼接结果
93
+ collages = process_images(uploaded_files)
94
+
95
+ # 如果没有生成任何拼接图,则返回 None,让前端不显示下载
96
+ if not collages:
97
+ return None
98
+
99
+ # 在内存中创建 zip
100
  buf = io.BytesIO()
101
+ with zipfile.ZipFile(buf, "w") as zf:
102
+ for idx, img in enumerate(collages):
103
+ # PIL 图像转成字节并写入 zip
104
+ img_bytes = io.BytesIO()
105
+ img.save(img_bytes, format="PNG")
106
+ img_bytes.seek(0)
107
+ zf.writestr(f"collage_{idx+1}.png", img_bytes.read())
108
  buf.seek(0)
109
+ return buf
110
 
111
+ ########################################
112
+ # 构建 Gradio 界面
113
+ ########################################
114
+ css_custom = """
115
+ /* 你可根据需要,在此自定义或覆盖 Gradio 的 CSS 样式 */
116
+ """
 
 
 
 
 
117
 
118
+ with gr.Blocks(css=css_custom) as demo:
119
+ gr.Markdown("## 图片 2×2 拼接小工具")
120
+ gr.Markdown(
121
+ "1. 请选择若干张图片(数量需为 4 的倍数才能完整拼接)。<br>"
122
+ "2. 每 4 张会被缩放并以 2×2 的形式合并成新图。<br>"
123
+ "3. 如果合并后图像尺寸超过 2048×2048,会自动缩小。<br>"
124
+ "4. 你可以在下方预览结果或打包下载。"
125
+ )
126
+
127
  with gr.Row():
128
+ with gr.Column():
129
+ file_input = gr.Files(label="上传图片(可多选)", file_types=["image"])
130
+ btn_preview = gr.Button("生成预览")
131
+ btn_zip = gr.Button("打包下载 ZIP")
132
+
133
+ with gr.Column():
134
+ gallery_output = gr.Gallery(label="拼接结果预览").style(grid=[2], height="auto")
135
+ zip_file = gr.File(label="下载拼接结果 ZIP", interactive=False, visible=False)
136
+
137
+ # 点击“生成预览” -> 显示拼接好的图片(可能多张)
138
+ btn_preview.click(
139
+ fn=process_images,
140
+ inputs=[file_input],
141
+ outputs=[gallery_output]
142
+ )
143
+
144
+ # 点击“打包下载 ZIP” -> 生成 zip 文件供下载
145
+ btn_zip.click(
146
+ fn=process_and_zip,
147
+ inputs=[file_input],
148
+ outputs=[zip_file]
149
+ )
150
 
151
  demo.launch()