STLooo commited on
Commit
716d72a
·
verified ·
1 Parent(s): 668c4c4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -97
app.py CHANGED
@@ -1,137 +1,127 @@
1
- import cv2
2
- import numpy as np
3
  import os
 
4
  import gradio as gr
 
5
  import zipfile
6
- import tempfile
7
  import gc
8
- from pdf2image import convert_from_path
9
- from PIL import Image
10
 
11
- # 設定常數與目錄
12
- BG_PATH = "background.jpg"
13
  FG_DIR = "foregrounds"
14
  OUTPUT_DIR = "output"
15
- ZIP_PATH = "output/results.zip"
 
16
  TARGET_WIDTH, TARGET_HEIGHT = 2133, 1200
17
  POS1 = (1032, 0)
18
  POS2 = (4666, 0)
19
 
20
- # 確保目錄存在
21
- for d in [FG_DIR, OUTPUT_DIR]:
22
- os.makedirs(d, exist_ok=True)
 
 
 
23
 
24
- # PDF 轉圖片
25
- def pdf_to_images(pdf_file_path, prefix):
26
- with open(pdf_file_path, "rb") as f:
27
- with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as tmp_pdf:
28
- tmp_pdf.write(f.read())
29
- tmp_pdf.flush()
30
- images = convert_from_path(tmp_pdf.name, fmt='jpg')
31
 
 
 
 
32
  paths = []
33
- for i, img in enumerate(images, 1):
34
- path = os.path.join(FG_DIR, f"{prefix}page_{i:03d}.jpg")
35
- img.save(path, 'JPEG')
36
- paths.append(path)
 
37
  return paths
38
 
39
- # 儲存上傳
40
- def save_uploaded_images(files, prefix):
41
- paths = []
42
- for i, file in enumerate(files, 1):
43
- img = Image.open(file.name).convert("RGB")
44
- path = os.path.join(FG_DIR, f"{prefix}img_{i:03d}.jpg")
45
- img.save(path, "JPEG")
46
- paths.append(path)
47
- return paths
48
-
49
- # 合成圖層
50
  def overlay_image(bg, fg, x, y):
51
  h, w = fg.shape[:2]
52
  if fg.shape[2] == 4:
53
  alpha = fg[:, :, 3:] / 255.0
54
  bg[y:y+h, x:x+w] = alpha * fg[:, :, :3] + (1 - alpha) * bg[y:y+h, x:x+w]
55
  else:
56
- bg[y:y+h, x:x+w] = fg[:, :, :3]
57
  return bg
58
 
59
- # 壓縮輸出成 ZIP
60
- def zip_output_files():
61
- with zipfile.ZipFile(ZIP_PATH, "w", zipfile.ZIP_DEFLATED) as zipf:
62
- for file in sorted(os.listdir(OUTPUT_DIR)):
63
- if file.endswith(".jpg"):
64
- zipf.write(os.path.join(OUTPUT_DIR, file), arcname=file)
65
  return ZIP_PATH
66
 
67
- # 主處理邏輯
68
- def process_inputs(pdf_file, image_files, prefix):
69
- try:
70
- progress = gr.Progress()
71
- fg_images = []
72
 
73
- # 清資料
74
- for f in os.listdir(FG_DIR):
75
- os.remove(os.path.join(FG_DIR, f))
76
- for f in os.listdir(OUTPUT_DIR):
77
  os.remove(os.path.join(OUTPUT_DIR, f))
78
 
79
- # PDF
80
- if pdf_file is not None:
81
- progress(0.1, "轉換 PDF 為圖片...")
82
- fg_images = pdf_to_images(pdf_file, prefix)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
- # 圖片
85
- elif image_files is not None:
86
- progress(0.1, "儲存上傳圖片...")
87
- fg_images = save_uploaded_images(image_files, prefix)
88
 
89
- else:
90
- raise gr.Error("請上傳 PDF 或圖片!")
91
-
92
- # 背景圖
93
- bg = cv2.imread(BG_PATH)
94
- if bg is None:
95
- raise gr.Error("找不到背景圖 background.jpg")
96
-
97
- results = []
98
- for i, img_path in enumerate(fg_images, 1):
99
- progress(0.2 + 0.7 * i / len(fg_images), f"合成第 {i} 張圖片...")
100
- fg = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
101
- fg = cv2.resize(fg, (TARGET_WIDTH, TARGET_HEIGHT))
102
- result = overlay_image(bg.copy(), fg, *POS1)
103
- result = overlay_image(result, fg, *POS2)
104
- out_path = os.path.join(OUTPUT_DIR, f"{prefix}result_{i:03d}.jpg")
105
- cv2.imwrite(out_path, result)
106
- results.append((out_path, f"{prefix}{i:03d}"))
107
- if i % 10 == 0:
108
- gc.collect()
109
-
110
- zip_file = zip_output_files()
111
- return results, zip_file, f"✅ 成功處理 {len(results)} 張圖片,ZIP 可下載"
112
-
113
- except Exception as e:
114
- raise gr.Error(f"處理出錯:{str(e)}")
115
-
116
- # Gradio UI
117
  with gr.Blocks() as demo:
118
- gr.Markdown("## 📄 PDF 或圖片合成工具(固定背景)")
119
 
120
  with gr.Row():
121
- pdf_file = gr.File(label="上傳 PDF(選一)", file_types=[".pdf"])
122
- image_files = gr.Files(label="或上傳圖片(JPG/PNG 多張)", file_types=[".jpg", ".png"])
 
123
 
124
- prefix_input = gr.Textbox(label="自定義檔名前綴", value="results_")
125
- run_btn = gr.Button("開始合成")
126
- gallery = gr.Gallery(label="預覽", columns=3)
127
- zip_file = gr.File(label="下載 ZIP")
128
- log = gr.Textbox(label="處理日誌", interactive=False)
129
 
130
- run_btn.click(
131
  fn=process_inputs,
132
- inputs=[pdf_file, image_files, prefix_input],
133
- outputs=[gallery, zip_file, log],
134
- concurrency_limit=2
135
  )
136
 
137
  if __name__ == "__main__":
 
 
 
1
  import os
2
+ import cv2
3
  import gradio as gr
4
+ import numpy as np
5
  import zipfile
6
+ import fitz # PyMuPDF
7
  import gc
 
 
8
 
9
+ # 參數設定
 
10
  FG_DIR = "foregrounds"
11
  OUTPUT_DIR = "output"
12
+ ZIP_PATH = os.path.join(OUTPUT_DIR, "results.zip")
13
+ BG_PATH = "background.jpg"
14
  TARGET_WIDTH, TARGET_HEIGHT = 2133, 1200
15
  POS1 = (1032, 0)
16
  POS2 = (4666, 0)
17
 
18
+ # 建立資料夾
19
+ def ensure_dir(path):
20
+ if not os.path.exists(path):
21
+ os.makedirs(path)
22
+ if not os.access(path, os.W_OK):
23
+ raise RuntimeError(f"❌ 沒有寫入權限:{path}")
24
 
25
+ ensure_dir(FG_DIR)
26
+ ensure_dir(OUTPUT_DIR)
 
 
 
 
 
27
 
28
+ # 將 PDF 每頁轉為圖檔
29
+ def pdf_to_images(pdf_file):
30
+ doc = fitz.open(pdf_file.name)
31
  paths = []
32
+ for i, page in enumerate(doc, start=1):
33
+ pix = page.get_pixmap(matrix=fitz.Matrix(2, 2))
34
+ img_path = os.path.join(FG_DIR, f"page_{i:03d}.jpg")
35
+ pix.save(img_path)
36
+ paths.append(img_path)
37
  return paths
38
 
39
+ # 圖像合成功能
 
 
 
 
 
 
 
 
 
 
40
  def overlay_image(bg, fg, x, y):
41
  h, w = fg.shape[:2]
42
  if fg.shape[2] == 4:
43
  alpha = fg[:, :, 3:] / 255.0
44
  bg[y:y+h, x:x+w] = alpha * fg[:, :, :3] + (1 - alpha) * bg[y:y+h, x:x+w]
45
  else:
46
+ bg[y:y+h, x:x+w] = fg
47
  return bg
48
 
49
+ # 壓縮 ZIP
50
+ def zip_results():
51
+ with zipfile.ZipFile(ZIP_PATH, "w", zipfile.ZIP_DEFLATED) as z:
52
+ for f in sorted(os.listdir(OUTPUT_DIR)):
53
+ if f.endswith(".jpg"):
54
+ z.write(os.path.join(OUTPUT_DIR, f), arcname=f)
55
  return ZIP_PATH
56
 
57
+ # 合成流程
58
+ def process_and_combine(fg_paths, prefix):
59
+ bg = cv2.imread(BG_PATH)
60
+ if bg is None:
61
+ raise gr.Error("❌ 無法讀取 background.jpg")
62
 
63
+ # 清
64
+ for f in os.listdir(OUTPUT_DIR):
65
+ if f.endswith(".jpg"):
 
66
  os.remove(os.path.join(OUTPUT_DIR, f))
67
 
68
+ results = []
69
+ for i, path in enumerate(fg_paths, 1):
70
+ fg = cv2.imread(path, cv2.IMREAD_UNCHANGED)
71
+ if fg is None:
72
+ continue
73
+ fg = cv2.resize(fg, (TARGET_WIDTH, TARGET_HEIGHT))
74
+ result = overlay_image(bg.copy(), fg, *POS1)
75
+ result = overlay_image(result, fg, *POS2)
76
+
77
+ out_path = os.path.join(OUTPUT_DIR, f"{prefix}{i:03d}.jpg")
78
+ cv2.imwrite(out_path, result)
79
+ results.append((out_path, f"{prefix}{i:03d}"))
80
+
81
+ if i % 10 == 0:
82
+ gc.collect()
83
+
84
+ if not results:
85
+ raise gr.Error("⚠️ 沒有生成任何圖片")
86
+
87
+ zip_file = zip_results()
88
+ return results, zip_file, f"✅ 成功合成 {len(results)} 張圖片"
89
+
90
+ # 主入口:支援圖片或 PDF 混合上傳
91
+ def process_inputs(files, prefix):
92
+ if not prefix.strip():
93
+ prefix = "result_"
94
+
95
+ fg_paths = []
96
+ for f in files:
97
+ name = f.name.lower()
98
+ if name.endswith(".pdf"):
99
+ fg_paths.extend(pdf_to_images(f))
100
+ elif name.endswith((".png", ".jpg", ".jpeg")):
101
+ fg_paths.append(f.name)
102
+ else:
103
+ raise gr.Error(f"不支援的檔案格式:{name}")
104
 
105
+ return process_and_combine(fg_paths, prefix)
 
 
 
106
 
107
+ # Gradio 介面
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  with gr.Blocks() as demo:
109
+ gr.Markdown("## 📄🖼️ PDF 或圖片合成工具(背景為 7872x1200)")
110
 
111
  with gr.Row():
112
+ upload_files = gr.Files(label="上傳 PDF 或圖片", file_types=[".pdf", ".png", ".jpg", ".jpeg"])
113
+ prefix_input = gr.Textbox(label="輸出檔案前綴 (選填)", placeholder="例如:project_")
114
+ btn_run = gr.Button("開始合成", variant="primary")
115
 
116
+ with gr.Row():
117
+ gallery = gr.Gallery(label="預覽", columns=3)
118
+ zip_file = gr.File(label="下載 ZIP")
119
+ log_output = gr.Textbox(label="處理日誌", interactive=False)
 
120
 
121
+ btn_run.click(
122
  fn=process_inputs,
123
+ inputs=[upload_files, prefix_input],
124
+ outputs=[gallery, zip_file, log_output]
 
125
  )
126
 
127
  if __name__ == "__main__":