【免费下载链接】FairScan
An Android app to scan your documents — simple and respectful
FairScan 是一款开源、隐私友好的 Android 文档扫描应用,所有处理都在本机完成。很多扫描应用的通病是:拍完照生成的 PDF 页面大小总是和真实纸张"差一口气"。本文带你拆解 FairScan 的纸张尺寸识别与自动吸附算法——它如何从一张歪斜的照片里估算出纸张的真实毫米尺寸,再把 212×300mm 这样的近似值一键"吸附"到 A4、Letter 等标准纸型,让扫描 PDF 一比一还原真实纸张。
扫描件为什么总"对不上" A4?
📷 用普通相机拍一张纸,照片里的"纸"是透视变形过的:近宽远窄、梯形、带阴影。如果直接把裁剪结果塞进 PDF,页面尺寸只能按像素拍脑袋,打印出来必然偏大或偏小。
FairScan 的解决思路分三步:先算出纸的真实尺寸(毫米),再吸附到标准纸型,最后把毫米写进 PDF。
三步管线:从一张歪斜照片到毫米级纸型
第 1 步:用透视几何估算"真实尺寸"
核心逻辑在 Perspective.kt 的estimateRealDimensions()函数里,它做了几件事:
- 求两组对边的消失点:纸张四条边在照片里是梯形,把上下边、左右边分别延长,交点就是消失点(vanishing point)。
- 恢复平面几何:结合相机焦距(若设备提供相机参数
CameraIntrinsics,否则用消失点反推焦距f² = -(v1x·v2x + v1y·v2y)),把四个角点"反投影"回三维空间,量出四条边的真实长度。 - 输出两种结果:
- 设备能提供主体距离(
subjectDistance)→ 得到Physical(单位:毫米) - 拿不到距离 → 只得到
Ratio(只有宽高比有意义)
- 设备能提供主体距离(
如果纸张几乎正对镜头、透视太弱(消失点飘到无穷远),代码会退回"上下边取平均"的保守估计,保证结果稳定。
第 2 步:自动吸附snapToStandardFormat
量出来的是近似值(相机测距、透视估算都有误差),所以 FairScan 把它"吸附"到最接近的标准纸型,见 Perspective.kt:
| 标准纸型 | 宽 × 高(mm) | 宽高比 |
|---|---|---|
| A3 | 297 × 420 | ≈1.414 |
| A4 | 210 × 297 | ≈1.414 |
| A5 | 148 × 210 | ≈1.419 |
| Letter | 215.9 × 279.4 | ≈1.294 |
| Legal | 215.9 × 355.6 | ≈1.647 |
判定条件(两个默认容差):
ratioTolerance = 0.04:宽高比误差 < 4%dimensionTolerance = 0.20:宽、高各自误差 < 20%
两个条件同时满足才算命中;命中横版纸张时还会自动交换宽高,保持方向正确。以上纸型定义在 Perspective.kt 的PaperFormats对象中。
第 3 步:把毫米写进 PDF 页面
导出时,AndroidPdfWriter.kt 按 PDF 规范(72 点/英寸,1 英寸 = 25.4mm)把毫米尺寸换算成 PDF 页面点数:
val pointsPerMm = 72f / 25.4f // ≈ 2.835 点/mm val widthPoints = widthMm.toFloat() * pointsPerMm同时constrainToMaxFormat()会把页面限制在 A4 范围内,防止异常尺寸撑爆版面。若某页拿不到物理尺寸,则退化为"按 A4 高度等比缩放"的兜底策略。
4% 容差是怎么定出来的?
🎯 关键洞察:标准纸型之间要么"宽高比差很多",要么"尺寸差很多",所以 4% + 20% 的双门槛足以避免误判:
- A4 vs Letter:宽高比 1.414 vs 1.294,相差约 12%,远超 4% 容差 → 一眼分辨
- A4 vs A5:宽高比几乎相同(1.414 vs 1.419),但尺寸差了约 30% → 靠 20% 的绝对尺寸容差拦截
- 远非标准纸:如 150×400mm 的怪尺寸,两项都超差 → 原样保留,不强行吸附
这套"比例 + 绝对尺寸"双校验,正是 EstimatedDimensionsTest.kt 中单元测试覆盖的场景:
| 输入(mm) | 吸附结果 |
|---|---|
| 212 × 300 | ✅ A4 (210×297) |
| 218 × 281 | ✅ Letter (215.9×279.4) |
| 300 × 212 | ✅ 横版 A4 (297×210) |
| 150 × 400 | 🚫 保持不变 |
常见疑问:什么时候会吸附失败?
- 设备缺少相机参数或测距数据→ 只能得到
Ratio(宽高比),snapToStandardFormat()直接跳过,PDF 用兜底缩放。 - 纸几乎正对镜头→ 消失点不可靠,退回"边长平均"估计,同样只得到比例。
- 纸张太倾斜(中心射线与纸面法线夹角余弦 < 0.1)→ 测距结果被判定不可信,降级为比例。
- 非标准纸张(手写便签、A4 剪裁件等)→ 保持估算原值,不吸附。
换句话说:FairScan 宁可不吸附,也不"猜错纸型",这与它"简单且尊重用户"的产品定位一致。
源码地图 🗺️
- 纸型定义与吸附算法:imageprocessing/src/main/java/org/fairscan/imageprocessing/Perspective.kt
- 尺寸估算调用处(裁剪管线):imageprocessing/src/main/java/org/fairscan/imageprocessing/DocumentDetection.kt
- 导出前尺寸计算:app/src/main/java/org/fairscan/app/domain/ExportPreparation.kt
- PDF 页面尺寸写入:app/src/main/java/org/fairscan/app/platform/AndroidPdfWriter.kt
- 吸附单元测试:imageprocessing/src/test/java/org/fairscan/imageprocessing/EstimatedDimensionsTest.kt
- 更新日志("Better PDF page sizes with automatic detection of standard paper formats"):metadata/en-US/changelogs/
📌一句话总结:FairScan 用"透视几何量真实毫米 + 4% 比例容差吸附标准纸型 + 毫米直写 PDF"这条管线,让扫描件无需任何手动调整,就能获得 A4/Letter 的精确页面尺寸——而且整个过程 100% 在你的手机上完成。
【免费下载链接】FairScan
An Android app to scan your documents — simple and respectful
相关推荐
突破A4限制:Xournal++自定义纸张尺寸全攻略
突破A4限制:Xournal++自定义纸张尺寸全攻略 你是否还在为标准纸张尺寸无法满足特殊需求而烦恼?无论是绘制大幅思维导图、设计工程图纸,还是创建个性化笔记模
桌面应用Puter prompt 对话框 API 完全指南:puter.ui.prompt() 用法、参数与实现原理
Puter prompt 对话框 API 完全指南:puter.ui.prompt 用法、参数与实现原理 导读 本文面向使用 Puter 平台(The Inte
从A4到Tabloid:pdfmake如何用52行代码定义全球纸张尺寸标准
从A4到Tabloid:pdfmake如何用52行代码定义全球纸张尺寸标准 pdfmake是一个强大的纯JavaScript库,支持在客户端和服务器端生成PDF
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考