Back to home

zjsthmjialin

pdf-background-gray-codex-skill

Codex skill for removing gray backgrounds from scanned PDFs without changing resolution or anti-aliased text.

Stars
1
Language
Python
Created
Jun 18, 2026
Updated
Aug 16, 2026

Introduction

PDF 去底灰(原分辨率)Codex Skill

remove-pdf-background-gray 是一个用于扫描型 PDF 的 Codex Skill。它可以去除纸张扫描产生的灰底或偏白底色,同时保持页面尺寸、嵌入图像的原始像素尺寸和文字的连续抗锯齿边缘。

这个项目既可以作为 Codex Skill 自动调用,也可以直接运行其中的 Python 脚本。

DeepSeek Harness (DSH) 插件安装

remove-pdf-background-gray 也是 DeepSeek Harness (DSH) 插件,一条命令安装:

dsh plugin --profile web add dsh-pdf-background-gray

重启 DSH Web 后,向 Agent 说"把这份扫描 PDF 去底灰"即自动走技能工作流(要求本机 Python 3.10+ 与 python -m pip install pypdf Pillow numpy)。

核心特点

  • 保持原分辨率:直接处理 PDF 内嵌图像,不缩放图像。
  • 不整页重绘:不先把 PDF 页面渲染成新图片再重新组装。
  • 保护文字边缘:使用连续的高光映射,不使用容易产生锯齿的硬阈值或黑白二值化。
  • 无损写回:处理后的图像使用 Flate 无损压缩写回 PDF,避免再次 JPEG 压缩文字边缘。
  • 内置验证:输出后检查页数、页面框和嵌入图像像素尺寸是否改变。
  • 安全中止:遇到内联图像或透明蒙版时停止处理,避免悄悄破坏复杂页面。

适用范围

适合以下情况:

  • 扫描书籍、档案、讲义或合同的纸张底色发灰。
  • 希望背景变白,但不希望文字被二值化或出现明显锯齿。
  • 需要保持原始 DPI、图像宽高和 PDF 页面尺寸。
  • PDF 页面主要由 JPEG、灰度图或 RGB 扫描图组成。

不建议直接用于:

  • 以矢量文字、复杂插画或透明图层为主的 PDF。
  • 灰色本身就是内容的一部分,例如铅笔画、浅灰表格、医学影像或艺术作品。
  • 需要极致压缩体积,而不是优先保证文字边缘质量的场景。
  • 带有透明蒙版、内联图像或特殊色彩空间且尚未人工检查的文件。

处理前建议使用 pdfinfopdffontspdfimages -list 检查 PDF 结构,并先对一页代表性内容做测试。

已验证效果

本 Skill 最初用于处理一份真实扫描 PDF,验证结果如下:

项目结果
PDF 页数194 页
处理的独立图像对象713 个
页面数量处理前后相同
页面尺寸与页面框处理前后相同
嵌入图像像素尺寸处理前后相同
文字边缘保留连续灰阶抗锯齿,未做黑白二值化
原文件大小48,838,427 字节,约 46.6 MiB
输出文件大小174,219,856 字节,约 166.1 MiB

背景灰度在代表页中主要集中于约 252。默认参数保持灰度 230 及以下不变,并将 230252 的高光区域平滑过渡到纯白。

文件体积增大是预期结果:原 PDF 的扫描图像主要使用有损 JPEG 压缩,而处理后为了避免再次损伤文字边缘,改用 Flate 无损压缩。不同 PDF 的体积变化会有明显差异。

上述结果来自一个具体文件,不代表所有 PDF 都会获得相同的视觉效果或体积变化。处理重要资料前请保留原文件并抽查输出。

在 Codex 中安装

方法一:通过 Git 克隆

将仓库克隆到 Codex 的个人 Skills 目录:

git clone https://github.com/zjsthmjialin/pdf-background-gray-codex-skill.git `
  "$HOME\.codex\skills\remove-pdf-background-gray"

如果仓库为私有状态,需要先配置 GitHub 凭据或使用 GitHub CLI 登录:

gh auth login

安装完成后重启 Codex,使新 Skill 被自动发现。

方法二:手动复制

把整个项目目录复制到:

%USERPROFILE%\.codex\skills\remove-pdf-background-gray

最终结构中应直接包含 SKILL.md,不要多嵌套一层目录。

在 Codex 中使用

可以显式调用 Skill:

使用 $remove-pdf-background-gray 处理 C:\path\input.pdf,去掉扫描底灰,保持原分辨率和文字抗锯齿。

也可以用自然语言触发:

把这个扫描 PDF 的页面底色去除,保持原分辨率不变,文字不要出现锯齿,只去底灰。

Codex 应先检查 PDF 类型和代表页,再调用脚本生成新文件并验证输出。

直接运行脚本

环境要求

  • Python 3.10 或更高版本
  • pypdf
  • Pillow
  • NumPy

安装依赖:

python -m pip install pypdf Pillow numpy

基本命令

在项目根目录运行:

python scripts/remove_pdf_background_gray.py `
  "C:\path\input.pdf" `
  "C:\path\output.pdf"

输入和输出路径必须不同。脚本不会覆盖原文件。

调整参数

python scripts/remove_pdf_background_gray.py `
  "C:\path\input.pdf" `
  "C:\path\output.pdf" `
  --low 235 `
  --white-point 250
参数默认值作用
--low230此值及以下保持不变;提高它可保护更多浅灰细节
--white-point252达到此值时映射为纯白;降低它会更积极地去除较深底灰

参数必须满足:

0 <= low < white-point <= 255

调参建议:

  • 背景仍然偏灰:逐步降低 --white-point,每次调整 2 到 5。
  • 浅灰线条或细节变淡:提高 --low,或提高 --white-point
  • 先用单页样本测试,不要直接对唯一原件批量处理。

技术原理

1. 直接处理嵌入图像

脚本使用 pypdf 读取 PDF,并定位每页引用的图像 XObject。共享的图像对象只处理一次,然后替换原对象的数据流。

这种方式不会把整页重新渲染,因此可以保留:

  • PDF 页面的物理尺寸和页面框。
  • 每个扫描图块的原始像素宽高。
  • 原有页面排版和图像定位关系。

2. 只处理高光区间

默认情况下,灰度值 230 及以下完全不变。只对 230252 之间的高光区域计算调整量。

映射使用 smoothstep 曲线:

t = clamp((value - low) / (white_point - low), 0, 1)
smooth = t * t * (3 - 2 * t)
result = value + (255 - value) * smooth

这条曲线在区间两端连续且变化平缓。与“超过某个值就直接变白”的硬阈值相比,它不会突然切断文字边缘的浅灰过渡。

对于 RGB 图像,脚本只处理接近中性灰的高光像素;通道差异较大的彩色像素不会被当作纸张灰底处理。

3. 无损写回

处理后的像素使用 /FlateDecode 写回 PDF:

  • 灰度图使用 /DeviceGray
  • RGB 图使用 /DeviceRGB
  • 每通道保持 8 bit。
  • 图像宽高保持不变。

Flate 是无损压缩,因此不会引入新的 JPEG 方块、振铃或文字边缘模糊。但对于扫描图像,它通常比 JPEG 占用更多空间。

4. 输出验证

脚本写出 PDF 后会重新打开文件,并检查:

  1. 页数是否一致。
  2. 每页 MediaBox 是否一致。
  3. 所有嵌入图像的像素宽高是否一致。

任何一项不一致都会抛出错误,而不是把未验证的文件当作成功结果。

制作过程

这个 Skill 来源于一次实际的 PDF 修复任务,主要步骤如下:

  1. 使用 pdfinfo 确认文件共有 194 页并读取页面尺寸。
  2. 使用 pdffonts 确认文件中没有字体对象,判断文字来自扫描图像。
  3. 使用 pdfimages -list 检查图像尺寸、DPI、颜色空间和压缩方式。
  4. 渲染代表页,观察纸张底色和文字边缘。
  5. 统计代表图块的灰度分布,确认背景主要集中在约 252。
  6. 放弃整页重新栅格化和硬阈值二值化方案。
  7. 采用直接替换图像对象、连续高光映射和 Flate 无损压缩。
  8. 对全部 194 页和 713 个独立图像对象执行处理。
  9. 核对页数、页面框和图像像素尺寸,并抽查前段、中段和末页渲染结果。
  10. 将可复用流程封装为脚本和 Codex Skill,并用官方 Skill 校验器检查目录结构。

验证建议

即使脚本内置结构验证,仍建议进行视觉检查:

  1. pdfinfo 比较输入和输出的页数、页面尺寸。
  2. pdfimages -list 抽查图像宽高和 DPI。
  3. 渲染首页、正文页、中间页和末页。
  4. 在 200% 到 400% 缩放下检查文字笔画边缘。
  5. 检查浅灰表格线、印章、插图和页面污渍是否被误处理。
  6. 确认输出可以被常用 PDF 阅读器完整打开。

安全机制与限制

  • 发现内联图像时,脚本会中止,因为当前实现不能安全地替换它们。
  • 发现 /SMask/Mask 透明蒙版时,脚本会中止,避免破坏透明关系。
  • LRGB 图像会转换为 RGB,特殊印刷色空间需要额外检查。
  • 脚本不执行 OCR,也不会创建可搜索文字层。
  • 脚本不会自动判断所有浅灰内容究竟是纸张背景还是有效信息。
  • 输出使用无损压缩,文件可能明显增大。
  • 当前实现依赖 pypdf 的部分内部对象接口;升级依赖后应重新运行样本测试。

常见问题

输出背景仍然偏灰

适当降低 --white-point。建议小步调整,并检查浅灰细节是否仍然完整。

浅灰表格线变淡

提高 --low--white-point,缩小被调整的高光范围。如果浅灰线与纸张底色亮度接近,自动处理无法完全区分两者。

输出文件变大

这是无损 Flate 替代有损 JPEG 后的常见结果。项目优先保证处理后的像素不再经历一次有损压缩。

提示没有找到嵌入图像

该 PDF 可能主要由矢量内容构成,或采用了当前脚本未支持的图像组织方式。不要强行处理,应先检查 PDF 结构。

提示存在透明蒙版或内联图像

这是保护性中止。需要为该类 PDF 设计专门处理流程,不能简单忽略提示。

项目结构

remove-pdf-background-gray/
├─ SKILL.md
├─ README.md
├─ agents/
│  └─ openai.yaml
├─ scripts/
│  └─ remove_pdf_background_gray.py
└─ docs/
   └─ superpowers/
      ├─ specs/
      │  └─ 2026-06-18-readme-design.md
      └─ plans/
         └─ 2026-06-18-readme-implementation-plan.md
  • SKILL.md:Codex 的触发条件、工作流程和安全约束。
  • agents/openai.yaml:Skill 在 Codex 界面中的名称、简介和默认提示词。
  • scripts/remove_pdf_background_gray.py:确定性的 PDF 图像处理和验证脚本。
  • README.md:面向使用者和开发者的项目说明。

贡献与反馈

欢迎提交 Issue 或 Pull Request。反馈问题时,建议提供以下信息:

  • Python、pypdf、Pillow 和 NumPy 版本。
  • PDF 页数、是否含字体对象及 pdfimages -list 摘要。
  • 使用的 --low--white-point 参数。
  • 可以公开的最小复现样本,或脱敏后的代表页。
  • 预期效果和实际效果的具体差异。

请勿上传包含个人隐私、合同机密或版权受限内容的原始 PDF。

联系作者

许可证

当前仓库尚未提供许可证文件。在明确添加许可证之前,代码默认保留全部权利;公开使用、修改或再分发前请先联系作者确认授权范围。