CTPimages / app.py
STLooo's picture
Update app.py
215cb68 verified
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()