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()