掃描 PDF 清潔器
用手機拍攝打開的書本,照片在書脊附近會彎曲、傾斜、灰暗,邊緣還可能出現拇指或下一頁的碎片。此技能能將這些頁面轉換成如乾淨印刷品般易讀的內容。
腳本負責像素處理,你負責判斷:標記每頁的角落、檢查被移除的內容,並決定文字區塊外的哪些標記是印刷的(保留)或不是(移除)。每個步驟都會產生聯絡表供你以圖片方式查看,請務必檢查,不要假設步驟一定會成功。
設定(每台機器只需執行一次)
SKILL=<此技能的資料夾路徑> # 存放此 SKILL.md 的資料夾
python3 -m venv ~/.cache/scanned-pdf-cleaner-venv
~/.cache/scanned-pdf-cleaner-venv/bin/pip install -q -r "$SKILL/requirements.txt"
PY=~/.cache/scanned-pdf-cleaner-venv/bin/python
以下每個指令都以 cd "$SKILL/scripts" && $PY <指令> ... 執行。請為每份文件建立一個新的空白工作資料夾(位於專案外,例如暫存或臨時資料夾),使用者的檔案僅供讀取。
工作流程
1. 準備
$PY prepare.py INPUT.pdf WORK # 或:一個圖片資料夾,或圖片檔案(HEIC 須先轉換)
輸出:WORK/src/p-NNN.jpg(直立照片)、WORK/grid/g-NNN.jpg(每張照片加上每 100 像素的紅色網格,並在四邊標示百位數),以及 WORK/rough.json(包含每張照片的預設邊框)。
如果照片是橫向或上下顛倒,請旋轉:$PY prepare.py --turn WORK p-003.jpg 90|180|270(順時針)。
2. 標記頁面角落(由你執行)
閱讀每張網格圖片。針對每張照片,找出屬於該文件的頁面,將其四個角落寫入 WORK/rough.json,使用原始像素座標,按閱讀順序的左上、右上、右下、左下排序。例如,網格標籤為橫向 7、縱向 12,則寫入 [700, 1200]。
{"001": {"src": "p-000.jpg", "quad": [[96,72],[930,92],[945,1468],[70,1480]]}}
- 鍵值為輸出頁面順序(
001、002……),保持與閱讀順序一致。 - 以頁面的紙張邊緣為準,而非文字。靠近書脊處,以頁面彎入裝訂線的位置為準。如果頁面超出照片範圍,則以照片邊緣為角落。
- 跨頁照片會產生兩個條目,共用相同的
src,例如"005"和"006"。 - 誤差在 30 像素內即可,下一步會自動貼合真實邊緣。
3. 貼合紙張邊緣
$PY refine.py WORK
輸出:WORK/quads.json 和 WORK/sheets/quads-NN.jpg:橘色 = 你的標記,紅色 = 貼合後的邊緣,綠色圓點 = 左上角。請閱讀這些輸出圖片。如果紅色輪廓跳到陰影、桌面或相鄰頁面上,請修正標記並重新執行此步驟。
4. 平整、色調調整與邊緣清潔
$PY clean.py WORK # 或:clean.py WORK 004 007 以重新處理特定頁面
對每個頁面,此步驟會:
- 找出文字行與對齊的欄位邊緣;
- 將文字區塊矯正為矩形,並為每行文字擬合一個平滑彎曲曲面,使彎曲線條變直;
- 將紙張調為白色、墨跡調為黑色;
- 移除觸及頁面邊緣的暗色標記:手指、書脊彎曲、折痕與下一頁碎片。
文字行少於 5 行的頁面(如標題頁或全頁插圖)則使用簡單的四角變形。輸出:WORK/pages/、WORK/blocks.json 和 WORK/sheets/review-NN.jpg,其中紅色 = 已移除、藍色 = 待你決定、綠色 = 文字區塊。
請閱讀檢視圖片。注意事項:
- 文字仍彎曲或頁面傾斜:標記有誤。修正
rough.json,然後重新對該頁執行 refine 與 clean。 - 綠色方框內的文字顯示為紅色:請告知使用者。邊緣清潔不會移除文字區塊內的文字,除非該文字位於頁面邊緣帶內。
5. 列出文字區塊外的標記
$PY flags.py WORK
輸出:WORK/flags/flags-N.jpg:每個文字區塊外的暗色標記都會有編號的圖塊。
6. 決定保留或移除(由你執行)
閱讀標記圖片,並將結果寫入 WORK/decisions.json:
{"keep": [0, 3, 4, 7], "strokes_only": [23], "erase": [{"page": "014", "box": [1500, 300, 80, 900], "mode": "strokes"}]}
- keep:任何印刷元素。包括頁碼、頁眉、章節編號、圖說、側邊標籤、表格線、圖表與繪圖。印刷字體清晰且規律。書中的繪圖也屬於印刷,即使是潦草的兒童塗鴉。
- remove:不列入陣列。包括手寫文字、筆記、污漬、斑點、指紋、灰色條紋與破損邊緣陰影。筆跡線條細、不均勻且常傾斜,與字體不符。
- strokes_only:印刷字元上有或旁邊有筆劃。僅移除高度超過 1.5 行高的筆劃。
- erase:以頁面像素為單位指定
pages/page-KEY.jpg上的方框,用於處理文字區塊內你被要求移除的標記。"mode": "all"會將該方框塗白;"strokes"則僅移除較高的筆劃。
在移除使用者可能想保留的內容前,先詢問使用者。這包括文字區塊內的行間筆記與底線,以及螢光筆。預設:移除邊緣筆記,保留文字區塊內的底線。當你無法判斷印刷與手寫時,也應詢問使用者,直接顯示圖塊,而非描述。
7. 完成
$PY finish.py WORK OUT.pdf # --no-frame 保留每頁原有的外框;--dpi 預設 300
功能說明:
- 將文字區塊外的所有內容塗白,保留的項目除外。
- 移除緊貼文字區塊兩側的細長筆括號。
- 將所有頁面調整為統一的頁面大小:
- 文字大小一致:遠近拍攝的照片會對齊。縮放依據文字區塊的寬度,若圖表使欄位變窄則依據行距。
- 左右邊距相等,因此原本靠邊或因書本厚度而偏移的內容會被置中。
- 每頁從相同的上邊距開始。
在交付之前,請閱讀 WORK/sheets/final-NN.jpg,檢查每頁是否有殘留污漬、被裁切的文字或誤保留的內容。然後將 PDF 路徑提供給使用者。除非使用者另有要求,否則將 PDF 儲存在輸入檔案旁(例如 <名稱>_clean.pdf)。
理想的成果
- 文字行從頭到尾水平且平整,包括最靠近書脊的那一行。
- 紙張純白、文字純黑,細筆劃完整保留。大面積深色繪圖保持填充飽滿。
- 無手指、陰影、相鄰頁碎片或斑點。左右邊距相等,且所有頁面文字大小一致。
實際使用筆記
- 矯正器依賴文字,因此最適合滿版對齊行的頁面。它適用於任何書寫系統,包括從右至左(希伯來文、阿拉伯文),因為它僅分析行形狀。
- 繪圖內部的短行(標籤)在擬合彎曲時會被忽略,因此圖表不會導致頁面變形。
- 佔滿頁面的大面積深色繪圖,若採用簡單色調調整會變成灰色。
tone()會從周圍紙張區域取得紙張估計值,避免此問題。 - 當頁面彎曲導致書脊處的幾個字母無法辨識時,照片已無資訊可恢復。請如實告知,不要憑空創造文字。
- 雙欄論文:文字區塊會跨越兩欄。請檢查檢視圖片,確認跨欄中的文字行已正確擬合。