STLooo's picture
Update app.py
f1926db verified
import os, cv2, zipfile, gc, gradio as gr
import numpy as np
import fitz # PyMuPDF
from PIL import Image
# 設定
OUTPUT_DIR = "output"
ZIP_PATH = os.path.join(OUTPUT_DIR, "results.zip")
BG_PATH = "background.jpg"
TARGET_WIDTH, TARGET_HEIGHT = 2133, 1200
POS1 = (1032, 0)
POS2 = (4666, 0)
def ensure_dir(path):
os.makedirs(path, exist_ok=True)
if not os.access(path, os.W_OK):
raise RuntimeError(f"❌ 無法寫入:{path}")
ensure_dir(OUTPUT_DIR)
# 解析頁碼範圍字串(如 "1-3,5,7")
def parse_page_selection(selection_text):
if not selection_text.strip():
return None
pages = set()
for part in selection_text.split(','):
part = part.strip()
if '-' in part:
start, end = map(int, part.split('-'))
pages.update(range(start, end + 1))
elif part.isdigit():
pages.add(int(part))
return sorted(pages)
# PDF轉圖片(np.array 格式,BGR)
def pdf_to_images(pdf_file, page_list=None):
images = []
with fitz.open(pdf_file.name) as doc:
total_pages = len(doc)
selected = page_list or list(range(1, total_pages + 1))
for page_num in selected:
if 1 <= page_num <= total_pages:
pix = doc[page_num - 1].get_pixmap(matrix=fitz.Matrix(2, 2), colorspace=fitz.csRGB)
img = np.frombuffer(pix.samples, dtype=np.uint8).reshape((pix.height, pix.width, 3))
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
images.append(img)
return images
# 合成圖片
def overlay_image(bg, fg_img, x, y):
fg = cv2.resize(fg_img, (TARGET_WIDTH, TARGET_HEIGHT), interpolation=cv2.INTER_AREA)
bg[y:y+TARGET_HEIGHT, x:x+TARGET_WIDTH] = fg
return bg
# 處理與合成流程
def process_and_combine(images, prefix):
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, img in enumerate(images, 1):
combined = overlay_image(bg.copy(), img, *POS1)
combined = overlay_image(combined, img, *POS2)
output_path = os.path.join(OUTPUT_DIR, f"{prefix}{i:03d}.jpg")
cv2.imwrite(output_path, combined)
results.append((output_path, f"{prefix}{i:03d}"))
if i % 10 == 0:
gc.collect()
if not results:
raise gr.Error("⚠️ 沒有產生圖片")
with zipfile.ZipFile(ZIP_PATH, "w", zipfile.ZIP_DEFLATED) as zipf:
for f in sorted(os.listdir(OUTPUT_DIR)):
if f.endswith(".jpg"):
zipf.write(os.path.join(OUTPUT_DIR, f), arcname=f)
return results, ZIP_PATH, f"✅ 成功合成 {len(results)} 張圖,已打包 ZIP"
# 主邏輯:上傳處理
def process_inputs(files, prefix, page_selection):
prefix = prefix.strip() or "result_"
selected_pages = parse_page_selection(page_selection)
images = []
for file in files:
name = file.name.lower()
if name.endswith(".pdf"):
images.extend(pdf_to_images(file, selected_pages))
elif name.endswith((".png", ".jpg", ".jpeg")):
img = Image.open(file).convert("RGB")
img = np.array(img)
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
images.append(img)
else:
raise gr.Error(f"不支援的檔案格式:{name}")
if not images:
raise gr.Error("⚠️ 沒有有效圖片可處理,請確認頁碼或檔案內容")
return process_and_combine(images, prefix)
# Gradio UI 元件定義
page_selection = gr.Textbox(label="選擇頁碼(例如:1,3,5-7)", placeholder="留空代表全部頁面")
# 介面建構
demo = gr.Interface(
fn=process_inputs,
inputs=[
gr.Files(label="📎 上傳 PDF 或圖片", file_types=[".pdf", ".png", ".jpg", ".jpeg"]),
gr.Textbox(label="檔名前綴(可選)", placeholder="例如:poster_"),
page_selection
],
outputs=[
gr.Gallery(label="🖼️ 預覽", columns=3, height="auto"),
gr.File(label="⬇️ 下載 ZIP"),
gr.Textbox(label="📄 處理日誌")
],
title="PDF 圖片合成工具",
description="📄🖼️ 將 PDF 或圖片套用固定背景合成為寬圖,背景為 7872x1200,支援 PDF 選擇頁數與多檔處理。"
)
if __name__ == "__main__":
demo.launch()