情境:前篇PDF離線合併程式,可預覽進行合併,但實務上可能有圖檔截圖需求,有A圖檔、B圖檔,要合併成單一AB合併圖檔,因此同樣也可毫不費吹灰之力,請AGY神器參照前篇作法;另外生成MergePng.py圖檔合併工具。 PROMPT提示詞(如:藍色部分)如下:
"C:\MergePDF.py" 請參照此PDF合併程式作法,協助生成PNG圖檔合併程式,同樣提供GUI界面,可供使用者選擇單一或多個PNG/JPEG圖檔,
最後加流水號,合併成單一圖檔(原則可PNG、JPEG),將來源多個檔合併,產出後(生成最高解析度之JPEG圖檔),程式檔名為MergePng.py
💜首次執行,可能會因為缺乏TK套件,可以自行將下面錯誤訊息貼給AGY進行詢問或請它代為協助下載安裝
Traceback (most recent call last):
File "C:\mergepng.py", line 3, in <module>
import _tkinter # If this fails your Python may not be configured for Tk
ImportError: DLL load failed while importing _tkinter: 找不到指定的模組。
💜呼叫合併程式 python MergePng.py
🏛️ 圖檔合併程式 架構全景圖
🔬 五大核心模組:底層數理與生活化比喻對位
1. 模組一:自動雷射切邊機 —— _trim_borders()
就像裁縫師拿到一塊布,周圍有一圈沒用的白色毛邊。裁縫師拿一張純白色的紙疊上去「照妖」,凡是跟白紙一模一樣的地方就一刀剪掉,只留下真正有圖案的布料。
# 讀取左上角原點底色,建立純色參考圖並做逐像素差分
bg = Image.new(im.mode, im.size, im.getpixel((0, 0)))
diff = ImageChops.difference(im, bg)
bbox = diff.getbbox() # 計算非零像素最小外接矩形 [x1, y1, x2, y2]
- 程式先讀取圖片左上角第 (0, 0) 個像素作為底色(通常為純白
(255, 255, 255))。 - 建立一張一模一樣大的純色假圖
bg。 - 用
ImageChops.difference做逐像素矩陣減法:|Pixel(x, y) - Background(x, y)|。只要兩者顏色相同,相減結果就是(0, 0, 0)(純黑)。 diff.getbbox()找出矩陣中所有「非零像素(即有實體圖案內容)」的最小外接矩形邊界[x1, y1, x2, y2],再以im.crop()精確裁出,徹底切除兩側與上下的虛擬留白!
2. 模組二:樂高積木等寬卡榫 —— 幾何縮放對齊
如果把一塊 10 公分寬的樂高積木和一塊 8 公分寬的積木上下黏在一起,8 公分的那塊兩邊一定會凹進去(這就是先前您看到的「左右多出 100 像素白邊」)。要消除凹陷,沖印機就得把 8 公分的積木等比例影印放大成 10 公分寬,讓上下積木兩側完全齊平。
target_width = max(im.width for im in processed_images) # 找到最寬的基準圖 (例如 1537px)
ratio = target_width / im.width # 計算等比放大係數 (1537 / 1336 = 1.15)
new_h = int(im.height * ratio) # 高度嚴格依比例連動放大
im.resize((target_width, new_h), Image.Resampling.LANCZOS)
- 為什麼不能硬拉? 如果只拉寬度而不改變高度,圖片內的文字和表格會被嚴重壓扁或拉寬。因此高度必須嚴格乘以縮放比例
ratio,確保長寬比(Aspect Ratio)絕對守恆。 - 內插演算法採用 Lanczos(蘭索斯濾波器):透過 Sinc 數學函數對相鄰 8×8 區域進行高級雙立方卷積採樣,確保放大後的文字線條依然銳利、不模糊、無馬賽克。
3. 模組三:無縫長卷軸拼接 —— 畫布疊加
沖印機鋪開一張長長的白色畫紙,寬度剛好等於 1537 像素,長度等於所有圖片高度的總和。接著從最上方開始,第 1 張貼在
y = 0,第 2 張緊貼在第 1 張的下邊界 y = height_1,一滴膠水空隙也不留。
merged_canvas = Image.new("RGBA", (total_w, total_h), (255, 255, 255, 255))
cur_offset = 0
for im in processed_images:
paste_x = (total_w - im.width) // 2
paste_y = cur_offset
merged_canvas.paste(im, (paste_x, paste_y), im)
cur_offset += im.height # 下一張緊接在前一張的尾端
- 因為前面已經透過等寬縮放把所有圖的寬度統一為
total_w,所以paste_x永遠精準為0,左右白邊被自然消除!
4. 模組四:最高解析度 JPEG 保真機制 —— 破除「破壞性壓縮」
一般相機或通訊軟體傳 JPEG 為什麼文字邊緣會有一圈圈髒髒的毛邊(蚊狀噪聲)?因為人眼對「黑白明暗」很敏感,但對「彩色細節」很遲鈍。常規 JPEG 壓縮為了省檔案大小,會偷偷把色彩資訊砍掉 75%(稱為 4:2:0 抽樣)。
rgb_canvas.save(
out_path,
format="JPEG",
quality=100, # 100% 最高量化表 (不做高頻 DCT 係數截斷)
subsampling=0, # 關鍵!0 代表 4:4:4 無色彩抽樣 (Full Chroma Sampling)
dpi=(300, 300), # 專業印刷級 300 DPI 輸出
)
subsampling=0的決定性威力:強制色彩資訊每一個像素都完整保留(4:4:4 全採樣),徹底杜絕圖表中細小文字、程式碼邊緣的模糊毛邊!
5. 模組五:檔案室自動取號機 —— _get_next_filename()
就像銀行的自動叫號機。你存了一張叫
Merged.jpg,下次再來存時,系統自動看到櫃子裡已經有這張了,就幫你打上標籤 Merged_001.jpg;如果 _001 也有了,就自動印出 Merged_002.jpg,永遠不會不小心把上一張覆蓋毀損。
# 自動辨識既有主檔名與流水號尾碼
m = re.match(r"^(.*?)(?:_(\d+))?$", name)
prefix = m.group(1) if m else name
# 依序探測直到找到硬碟未使用的檔案序號
candidate = os.path.join(dir_name, f"{prefix}_{idx:03d}{ext}")
🎯 總結:問題與解法的物理映射清單
| 現象 | 物理根源 | MergePNG.py 的技術解法 |
|---|---|---|
| 合併後左右多出 100px 白邊 | 各張圖原始解析度不同(1537 vs 1336),直接置中會留白補齊 | 等寬縮放(Rescale):將寬度統一投影放大至 1537px,左右白邊歸零 |
| 原本圖片周圍有黑/白邊緣 | 截圖時不慎留下的外圍邊界 | _trim_borders 自動剪裁:矩陣相減求最小有效 Bounding Box 自動去邊 |
| 畫面右下角出現「1 2 雜訊」 | 原程式畫面印章功能被預設啟動(value=True) |
畫面印章預設關閉(False),改為純淨合併,並支援檔名後綴自動流水號 |
| JPEG 轉存文字毛邊 | JPEG 預設色彩抽樣 4:2:0 壓縮損失 | subsampling=0 + quality=100:強制 4:4:4 全色彩不降採樣保真輸出 |
💢其他提示詞發想運用(筆者沒有實際TRY):
💜如TIF圖檔,多個TIF檔,可以比照PDF合併,成為單一TIF檔。
💜螢幕整個視窗快照截圖,每個視窗畫面受限解析度版面設定,單一張截圖,也將可能跨下一個版面因而受限無法做完整截圖,而透過視窗快照不用切換螢幕解析度相對就方便實用。
相關資訊:
HEIC 2 JPG 圖檔轉換(1鍵完成數百筆HEIC圖檔轉換)