Spaces:
Runtime error
Runtime error
File size: 4,510 Bytes
ad22797 14c46f5 af3dd3e 4bbef93 14c46f5 4bbef93 14c46f5 4bbef93 14c46f5 4bbef93 14c46f5 4bbef93 14c46f5 4bbef93 af3dd3e 4bbef93 14c46f5 4bbef93 14c46f5 af3dd3e 4bbef93 14c46f5 4bbef93 14c46f5 4bbef93 14c46f5 4bbef93 14c46f5 4bbef93 14c46f5 af3dd3e 4bbef93 14c46f5 4bbef93 14c46f5 af3dd3e 4bbef93 14c46f5 4bbef93 14c46f5 4bbef93 14c46f5 af3dd3e 14c46f5 af3dd3e 4bbef93 14c46f5 4bbef93 14c46f5 af3dd3e 14c46f5 4bbef93 14c46f5 4bbef93 14c46f5 4bbef93 14c46f5 4bbef93 14c46f5 4bbef93 14c46f5 af3dd3e |
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 |
import gradio as gr
from PIL import Image
import io
import zipfile
def resize_image_to_multiple_of_64_rgba(img: Image.Image):
w, h = img.size
# 计算最接近的 64 倍数(保底64)
w64 = max(64, round(w / 64) * 64)
h64 = max(64, round(h / 64) * 64)
# 按最小缩放比,使原图能贴进 w64 x h64
scale = min(w64 / w, h64 / h)
new_width = int(w * scale)
new_height = int(h * scale)
# 用 RGBA、黑色背景(全不透明)
background = Image.new("RGBA", (w64, h64), (0, 0, 0, 255))
# 缩放原图到 new_width, new_height
scaled_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
# 贴到背景中央,保留其透明度
offset_x = (w64 - new_width) // 2
offset_y = (h64 - new_height) // 2
background.paste(scaled_img, (offset_x, offset_y), scaled_img)
return background
def make_collage_2x2(four_rgba_images):
# 4 张图,均已是 RGBA、同尺寸
w, h = four_rgba_images[0].size
# 新画布:2x2 => 宽度 2*w,高度 2*h,背景黑
collage = Image.new("RGBA", (2 * w, 2 * h), (0, 0, 0, 255))
collage.paste(four_rgba_images[0], (0, 0), four_rgba_images[0])
collage.paste(four_rgba_images[1], (w, 0), four_rgba_images[1])
collage.paste(four_rgba_images[2], (0, h), four_rgba_images[2])
collage.paste(four_rgba_images[3], (w, h), four_rgba_images[3])
cw, ch = collage.size
# 若合并后任意边 > 2048,则等比例缩小
if cw > 2048 or ch > 2048:
scale = min(2048 / cw, 2048 / ch)
new_cw = int(cw * scale)
new_ch = int(ch * scale)
collage = collage.resize((new_cw, new_ch), Image.Resampling.LANCZOS)
return collage
def process_images_for_preview(uploaded_files):
pil_images = []
for f in uploaded_files:
if f is not None:
# 以 RGBA 读图,保证可保留透明通道
img = Image.open(f.name).convert("RGBA")
pil_images.append(img)
results = []
# 每 4 张为一组,不足 4 张跳过
for i in range(0, len(pil_images), 4):
group = pil_images[i : i + 4]
if len(group) < 4:
break
# 每张做64倍数 resize + 黑色背景填充
resized = [resize_image_to_multiple_of_64_rgba(im) for im in group]
# 再 2×2 拼接
collage = make_collage_2x2(resized)
results.append(collage)
return results
def process_and_zip_for_download(uploaded_files):
collages = process_images_for_preview(uploaded_files)
if not collages:
# 没有任何拼接图,就返回 None,让界面不显示可下载链接
return None
buf = io.BytesIO()
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
for idx, img in enumerate(collages):
# 转字节并写入 zip
img_bytes = io.BytesIO()
# 以 PNG 格式保存,带 RGBA
img.save(img_bytes, format="PNG")
img_bytes.seek(0)
zf.writestr(f"collage_{idx+1}.png", img_bytes.read())
buf.seek(0)
return buf
with gr.Blocks() as demo:
gr.Markdown("## 图片 2×2 拼接小工具")
gr.Markdown(
"1. 一次可上传多张图片,每 4 张为一组。\n"
"2. 每组里 4 张图会先缩放到 64 的倍数、空余处用黑色填充,然后 2×2 拼接。\n"
"3. 拼接结果若超过 2048×2048,则缩小到不超过 2048。\n"
"4. 不足 4 张的剩余图片忽略。\n"
"5. 支持保留 PNG 透明度,拼图时使用黑色背景。"
)
with gr.Row():
with gr.Column():
file_input = gr.Files(label="上传图片(可多选)", file_types=["image"])
preview_btn = gr.Button("生成预览")
download_btn = gr.Button("打包下载 ZIP")
with gr.Column():
# Gallery 构造上不使用 .style() 以兼容老版本
gallery_output = gr.Gallery(label="拼接结果预览", columns=2)
zip_output = gr.File(label="下载拼接结果 ZIP", interactive=False, visible=False)
# 点击“生成预览” -> 返回拼接图列表
preview_btn.click(
fn=process_images_for_preview,
inputs=[file_input],
outputs=[gallery_output]
)
# 点击“打包下载 ZIP” -> 生成 zip
download_btn.click(
fn=process_and_zip_for_download,
inputs=[file_input],
outputs=[zip_output]
)
demo.launch() |