- 图像处理
- 开发工具
【免费下载链接】make-look-scanned
Makes PDFs look scanned (CLI or in the browser via WASM)
make-look-scanned 是一个 Go 编写的 PDF 扫描效果生成工具:它把一份电子 PDF 逐页"劣化"成扫描件的样子——倾斜、灰度、暖纸色、扫描颗粒、失焦模糊、边缘阴影,再加上低质量 JPEG 压缩痕迹。它既能作为命令行 CLI 使用,也能通过 WebAssembly 在浏览器里本地运行,全程不上传文件。
本文带你从像素级操作开始,完整拆解它的 6 大图像算法,看懂"电子文档"是如何一步步变成"真实纸张质感"的。
效果流水线:顺序为什么决定真实感
核心思想在 internal/effects/pipeline.go 中一句话讲清:每个效果是一个独立阶段,对应一个 CLI 参数;数值参数为 0 时该阶段自动关闭。
流水线按固定顺序执行,这个顺序本身就是"算法设计":
- 灰度 → 2. 纸张色调 → 3. 高斯模糊 → 4. 噪声 → 5. 边缘阴影 → 6. 倾斜旋转
页面先"做旧"、再旋转,这样之前所有效果都会跟着纸张一起歪斜,而不是贴在画面上的死板滤镜。第 7 步 JPEG 压缩则放在组装阶段(见下文),因为低质量压缩本身就是"扫描仪"最露馅也最可信的特征。
🎯 另一个容易被忽略的细节:每个随机效果(噪声、倾斜)各自持有独立的随机数生成器,且播种顺序固定。这样噪声的随机数消耗不会因为 DPI 变化(像素更多)而干扰倾斜角度,保证"同一输入 + 同一种子 = 字节级一致的 PDF"。
6大算法逐个拆解
以下每个算法都对应一个源文件,位于 internal/effects/ 目录,全部用纯 Go 手写像素运算,不依赖图像处理库。
① Grayscale:把整页压成单一亮度
源码:internal/effects/grayscale.go
算法只有一行核心逻辑:对每个像素按 Rec.601 亮度公式计算灰度值:
Y = 0.299R + 0.587G + 0.114B然后 R、G、B 三通道全部赋为 Y。这是人眼感知最接近真实的去饱和方式,也是老式扫描仪默认输出的"传真灰"基础。
② Paper tone:暖白纸色 + 黑色抬升
源码:internal/effects/papertone.go
真实纸张不是纯白。这个算法做两件事:
- 白纸变暖白:把纯白 (255,255,255) 映射到纸色 (250, 247, 236) —— 略带米黄的 off-white;
- 黑色抬升:最黑的墨迹不再是 0,而是抬升 18 个亮度等级,变成"扫描灰"。
参数--paper-tone(0~1)控制原图与调色结果的混合强度。这一步让整页瞬间从"屏幕感"变成"打印纸感"。
③ Blur:可分离高斯模糊模拟失焦
源码:internal/effects/blur.go
实现教科书级的可分离高斯卷积:
- 根据 sigma 生成 1D 高斯核(半径取
ceil(3σ),权重归一化); - 先做水平方向卷积到临时缓冲,再做垂直方向卷积回结果。
相比直接做 2D 卷积,可分离写法把复杂度从 O(n²·σ²) 降到 O(n²·σ),性能提升巨大。sigma 即 CLI 的--blur参数,模拟扫描仪光学系统的轻微失焦。
④ Noise:高斯分布的扫描颗粒
源码:internal/effects/noise.go
对每个像素加上一个高斯随机数,标准差由参数线性放大:stddev = noise × 40。
关键在三通道加同一个噪声值(n同时加给 R/G/B),这样噪声只改变亮度、不改变色相——这正是真实 CCD 传感器读出噪声的表现,而不是花屏。
⑤ Edge shadow:纸张微微翘起的边缘暗影
源码:internal/effects/edgeshadow.go
模拟纸张没有完全贴合玻璃、边缘微微翘起造成的边缘暗角(vignette):
- 影带宽度取页面短边的 12%;
- 每个像素到最近边缘的距离 d 决定变暗程度,边缘最深、向内线性淡出到 0;
- 远离边缘的像素直接跳过,不做无谓计算。
⑥ Skew:随机小角度旋转 + 双线性抗锯齿
源码:internal/effects/skew.go
扫描时纸放歪了——这是最容易被识别、也最难伪造的特征。算法:
- 在 ±
--skew度内随机取一个角度; - 对每个目标像素做反向旋转,回到源图坐标;
- 用双线性插值采样:落在图像外的邻近点贡献纸色 (250,247,236) 而不是钳制到 0,于是旋转后的页面边缘自然与纸底色混合,形成平滑的抗锯齿边线,而非锯齿状硬边。
💡 "反向映射 + 亚像素采样"是旋转类效果的通用正确姿势,正向映射会留下空洞像素。
隐藏的第七步:JPEG 压缩痕迹
前 6 个算法处理完,页面还是"无损的"。真正让它像扫描件的是组装阶段的低质量 JPEG 重编码,源码见 internal/assemble/assemble.go:
- 每页编码为 JPEG(默认质量 70,可低至 1),直接嵌入图片型 PDF;
- 页面尺寸保留 PDF 原始的 point 尺寸,非标准页幅不丢精度;
- 特意选用 gopdf 而非 fpdf,因为它按确定顺序序列化、不打时间戳——这是"确定性输出"的最后一环。
快速上手:一条命令生成扫描件
go build -o make-look-scanned . ./make-look-scanned in.pdf # 输出 in.scanned.pdf ./make-look-scanned in.pdf --noise 0.4 --skew 2.5 --jpeg-quality 30主要参数一览(数值为 0 即关闭对应效果):
| 参数 | 默认值 | 对应算法 |
|---|---|---|
--skew | 0.6 | ⑥ 倾斜角度上限(度) |
--grayscale | true | ① 灰度(唯一布尔参数) |
--paper-tone | 0.6 | ② 暖纸色调强度 0~1 |
--blur | 0.4 | ③ 高斯 sigma |
--noise | 0.08 | ④ 扫描颗粒 0~1 |
--edge-shadow | 0.15 | ⑤ 边缘暗角 0~1 |
--jpeg-quality | 70 | ⑦ JPEG 质量 1~100 |
--dpi | 150 | 渲染分辨率(最高 1200) |
--seed | 内容哈希 | 随机种子 |
所有参数会先经过Validate()统一校验(见 pipeline.go 第 44~62 行),超范围直接报错,避免浪费渲染时间。
配置预设:把常用参数存成 config.toml
在~/.make-look-scanned/config.toml(或$XDG_CONFIG_HOME/make-look-scanned/config.toml)中定义预设:
[presets.medium] skew = 1.5 paper_tone = 0.6 noise = 0.2 blur = 0.6 edge_shadow = 0.3 jpeg_quality = 45之后./make-look-scanned --preset medium in.pdf即可。优先级为:内置默认 → 预设 → 显式 CLI 参数(后者永远胜出),实现逻辑在 internal/config/config.go。
为什么同一份文件总生成同样的扫描件?
默认情况下种子由输入 PDF 的字节内容用 FNV-64a 哈希得出(见 main.go 的contentSeed),因此:
- 同一 PDF 反复运行 → 完全相同的扫描件(方便复现、便于比对);
- 传
--seed N→ 换一个可复现的"长相"; - 同一输入 + 同一种子 →字节级一致的 PDF。
同一套算法跑在浏览器里
由于 go-fitz/MuPDF 无法编译到 wasm,浏览器版换用 PDF.js 渲染页面,但拿到像素后交给同一套 Go 效果算法(编译为 wasm),入口在 cmd/wasm/main_js.go,页面在 web/index.html:
./web/build.sh # 开发模式 task build:web # 打包成单文件 dist/make-look-scanned.html单文件 HTML 内联了 wasm、Go 运行时胶水代码和 PDF.js,双击即可离线使用。浏览器版与 CLI 视觉效果一致(渲染器不同,字节不保证一致)。
小结
make-look-scanned 的魅力在于"用工程换真实":灰度、暖纸色、失焦、颗粒、暗角、歪斜六个算法各自只解决一个物理现象,顺序和随机性又都经过精心设计,最后用低质量 JPEG 收尾。理解它 6 大算法的实现,你对图像流水线(可分离卷积、反向映射采样、按通道一致的噪声、确定性随机播种)也会多一层实战认知。
- 图像处理
- 开发工具
【免费下载链接】make-look-scanned
Makes PDFs look scanned (CLI or in the browser via WASM)
相关推荐
SQLite3 can only bind 报错完整排查:HeyGem.ai 添加语音模型,1 个根因 3 种修法
SQLite3 can only bind 报错完整排查:HeyGem.ai 添加语音模型,1 个根因 3 种修法 HeyGem.ai 添加语音模型时弹出的 S
AI 应用人工智能数字人语音音频make-look-scanned快速教程:3分钟把PDF变成「逼真扫描件」,完整入门指南
make look scanned快速教程:3分钟把PDF变成「逼真扫描件」,完整入门指南 make look scanned 是一个开源 PDF 扫描件生成工
图像处理开发工具为什么nnAudio的可训练滤波器是音频深度学习的游戏规则改变者?终极指南揭秘
为什么nnAudio的可训练滤波器是音频深度学习的游戏规则改变者?终极指南揭秘 在音频深度学习的领域中, nnAudio的可训练滤波器 正在彻底改变游戏规则!这
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考