File size: 4,151 Bytes
fbec382 215cb68 5c7f834 d75c8c9 215cb68 6717890 215cb68 57ec27f fbec382 215cb68 57ec27f 215cb68 57ec27f 215cb68 fbec382 57ec27f 215cb68 57ec27f fbec382 215cb68 57ec27f 77273f7 215cb68 49b4baa 77273f7 57ec27f 215cb68 1a75a8f 57ec27f 215cb68 57ec27f 1a75a8f 215cb68 57ec27f 215cb68 77273f7 57ec27f 215cb68 57ec27f 215cb68 57ec27f 215cb68 5c7f834 215cb68 57ec27f 215cb68 49b4baa 57ec27f 5c7f834 215cb68 fbec382 215cb68 57ec27f 215cb68 57ec27f fbec382 57ec27f d47182a 215cb68 57ec27f 5c7f834 fbec382 d47182a | 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 | import cv2
import numpy as np
import os
import gradio as gr
import gc
import zipfile
# 常量設定
MAX_FILES = 250
BG_PATH = "background.jpg" # 預設背景圖,請自行放置
FG_DIR = "foregrounds"
OUTPUT_DIR = "output"
ZIP_PATH = "output/results.zip"
TARGET_WIDTH, TARGET_HEIGHT = 2133, 1200
POS1 = (1032, 0)
POS2 = (4666, 0)
# 初始化資料夾與權限檢查
def check_write_permission(path):
if not os.path.exists(path):
try:
os.makedirs(path)
except Exception as e:
raise RuntimeError(f"❌ 無法建立目錄 '{path}':{e}")
elif not os.access(path, os.W_OK):
raise RuntimeError(f"❌ 目錄 '{path}' 存在,但沒有寫入權限!")
check_write_permission(FG_DIR)
check_write_permission(OUTPUT_DIR)
# 合成圖片
def overlay_image(bg, fg, x, y):
h, w = fg.shape[:2]
bh, bw = bg.shape[:2]
if x + w > bw or y + h > bh:
raise ValueError(f"前景圖超出背景邊界:({x},{y},{w},{h}) > 背景({bw},{bh})")
if fg.shape[2] == 4:
alpha = fg[:, :, 3:] / 255.0
bg[y:y+h, x:x+w] = alpha * fg[:, :, :3] + (1 - alpha) * bg[y:y+h, x:x+w]
else:
bg[y:y+h, x:x+w] = fg[:, :, :3]
return bg
# 壓縮成 zip
def zip_output_files():
with zipfile.ZipFile(ZIP_PATH, "w", zipfile.ZIP_DEFLATED) as zipf:
for file in sorted(os.listdir(OUTPUT_DIR)):
if file.endswith(".jpg"):
zipf.write(os.path.join(OUTPUT_DIR, file), arcname=file)
return ZIP_PATH
# 處理合成流程
def process_images(fg_files):
try:
progress = gr.Progress()
progress(0, desc="準備中...")
if len(fg_files) == 0:
return [], None, "請至少上傳一張前景圖"
if len(fg_files) > MAX_FILES:
raise gr.Error(f"最多支援 {MAX_FILES} 張圖片")
bg = cv2.imread(BG_PATH)
if bg is None:
raise gr.Error("無法讀取背景圖(請確認 background.jpg 是否存在)")
# 清除舊結果
for f in os.listdir(OUTPUT_DIR):
if f.endswith(".jpg"):
os.remove(os.path.join(OUTPUT_DIR, f))
results = []
for i, fg_file in enumerate(fg_files, 1):
progress(i / len(fg_files), f"合成第 {i} 張圖...")
try:
with open(fg_file.name, 'rb') as f:
fg_data = np.frombuffer(f.read(), np.uint8)
fg = cv2.imdecode(fg_data, cv2.IMREAD_UNCHANGED)
if fg is None:
continue
fg = cv2.resize(fg, (TARGET_WIDTH, TARGET_HEIGHT), interpolation=cv2.INTER_AREA)
result = overlay_image(bg.copy(), fg, *POS1)
result = overlay_image(result, fg, *POS2)
output_path = f"{OUTPUT_DIR}/result_{i:03d}.jpg"
cv2.imwrite(output_path, result)
results.append((output_path, f"Result {i:03d}"))
if i % 10 == 0:
gc.collect()
except Exception as e:
print(f"處理 {fg_file.name} 失敗: {str(e)}")
continue
progress(1, "完成!")
zip_file = zip_output_files()
return results, zip_file, f"成功處理 {len(results)}/{len(fg_files)} 張圖片,已打包為 ZIP 可下載"
except Exception as e:
raise gr.Error(f"處理出錯:{str(e)}")
# Gradio 介面
with gr.Blocks() as demo:
gr.Markdown("## 🖼️ 寬幅圖片合成工具(背景固定為 7872x1200)")
fg_upload = gr.Files(label="上傳前景圖(JPG 或 PNG,自動套用背景)", file_types=[".jpg", ".png"])
btn_run = gr.Button("開始合成", variant="primary")
with gr.Row():
gallery = gr.Gallery(label="合成結果預覽", columns=3, preview=True)
zip_download = gr.File(label="下載結果 ZIP 檔")
log_output = gr.Textbox(label="日誌", interactive=False)
btn_run.click(
fn=process_images,
inputs=fg_upload,
outputs=[gallery, zip_download, log_output],
concurrency_limit=4
)
if __name__ == "__main__":
demo.launch() |