惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
罗磊的独立博客
M
MIT News - Artificial intelligence
G
Google Developers Blog
V
V2EX
D
Docker
博客园_首页
The Cloudflare Blog
人人都是产品经理
人人都是产品经理
Y
Y Combinator Blog
WordPress大学
WordPress大学
T
Tailwind CSS Blog
博客园 - 司徒正美
J
Java Code Geeks
L
LangChain Blog
博客园 - 三生石上(FineUI控件)
B
Blog RSS Feed
博客园 - 【当耐特】
小众软件
小众软件
Apple Machine Learning Research
Apple Machine Learning Research
大猫的无限游戏
大猫的无限游戏
P
Proofpoint News Feed
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - Franky

博客园 - 程序员李铁牛

赛事报名系统开发总管理中心规划 赛事报名系统开发:角色与权限体系设计 赛事系统报名接龙高并发技术细节处理浅析 档案数字化研发手记:PDF合并内存溢出踩坑 纸质档案数字化管理系统研发手记:法院诉讼档案单套制落地案例复盘——从纸质卷宗到电子卷宗 档案数字化管理系统开发手记:人事档案专项审核与数字化的系统支撑——"凡提必审"下的技术要点 档案数字化研发手记:我们的技术栈选择——档案系统前后端选型理由(含一次"炫技"的教训) 社群团购系统开发分享——工程化(下):资金支付测试策略——资金模块 100% 覆盖,其他 60% 就够 社群团购系统类快团团源码开发——工程化(上):一套让 5 人小团队不出事故的开发规范 社群团购类快团团系统开发——RBAC 权限设计源码分析:当一个微信号有 4 种身份时怎么办 社群团购系统类快团团模式开发——微信小程序登录:code2Session 之后还有 5 件事要做 数字档案管理系统化研发手记:医院病历档案数字化——合规、病案首页 OCR 与调阅提速 数字档案系统研发手记:批量扫描任务调度——TWAIN/SANE 采集驱动的封装实践 档案数字化系统研发源码手记:置信度过滤——把 97% 识别率变成真正可交付的成果 档案数字化开发实例手记:盖章遮挡文字识别恢复 景区运营预约系统开发:景区会员体系怎么搭?3级会员模型+积分玩法 景区门票预约系统全渠道平台库存数据同步技术处理方式 系统宕机了怎么办?景区票务系统应急预案模板 景区门票预约系统开发:接入AI预测客流设计思路分析 景区预约系统开发总结:门票分时预约设计原理和落地方法 设备运维管理系统开发实战:用规则引擎实现可配置的设备告警策略自研轻量引擎 vs Drools 落地对比 设备维修保养系统预测性维护不用深度学习?设备健康度评分的务实实现方案 档案数字化管理系统研发手记:档案权限模型——全宗-门类-案卷-文件四级控制的设计与实践 档案管理信息化系统研发手记:数字水印方案对比——可见水印 vs 盲水印(档案借阅防泄露) 景区票务系统开发实例分析:上云还是本地部署?6个维度判断 一物一码防伪溯源系统开发全栈源码串联一次扫码的完整链路追踪代码走读 景区门票预约系统票务系统的6个核心模块 景区上线门票预约系统的5个常见翻车点 景区门票预约系统之人脸识别 vs 身份证核验 vs 扫码入园,哪种最适合你? 景区门票预约系统开发深度解析之定价策略功能设计
档案数字化系统源码研发手记:老旧档案去污点——中值滤波与 in...
程序员李铁牛 · 2026-08-28 · via 博客园 - 程序员李铁牛

一、为什么档案去污点是个"必须做但又不能乱做"的活

老旧档案的污点来源五花八门:霉斑、水渍、油渍、虫蛀、胶带残留、墨水滴落、翻阅留下的指纹。这些污点对 OCR 的影响分两种:

  • 浅色污渍(水渍、霉斑浅层):干扰图像质量但文字仍可读;
  • 深色污点(墨滴、虫蛀孔、胶带黑影):直接遮挡文字,或者让 OCR 误检出大量"伪文本框"。

01-全流程地图-系统界面

档案数字化有一个铁律:处理过程可以提升可读性,但绝不能改变原始信息(依据 DA/T 31-2017 关于图像处理的要求,加工后的数字图像应保持与原件的"唯一性对应",不允许修改档案内容)。所以去污点的原则是:只去"噪声",不碰"文字"

二、两大家族:滤波 vs 图像修复(inpainting)

滤波派(中值滤波、高斯滤波、均值滤波)

思路:用邻域统计值替换每个像素,把孤立的小噪点"抹平"。

import cv2

# 中值滤波:对椒盐噪声(孤立的黑白点)效果最好
img_med = cv2.medianBlur(img, 5)   # 5x5 窗口

# 高斯滤波:对均匀噪声平滑,但会模糊边缘
img_gau = cv2.GaussianBlur(img, (5, 5), 0)

优点:速度快(300dpi A4 单页 <100ms)、无需训练、实现简单。
致命缺点:窗口越大越模糊,文字笔画也跟着糊;对面积超过几个像素的污点(霉斑往往成片)完全无效——滤波只是把污点边缘糊化,污点主体还在。

修复派(inpainting:Telea / 基于边缘的 FMM / 深度学习)

思路:把污点区域"抠出来"当蒙版,用周围有效像素的信息把空洞补起来。OpenCV 内置两种:

import cv2
import numpy as np

def inpaint_dots(img, mask):
    """mask:污点区域的二值蒙版(污点为白色255)"""
    # 方法1:Telea(基于快速行进法,默认)
    result1 = cv2.inpaint(img, mask, inpaintRadius=3, flags=cv2.INPAINT_TELEA)
    # 方法2:FMM(基于纳维-斯托克斯方程边缘传播)
    result2 = cv2.inpaint(img, mask, inpaintRadius=3, flags=cv2.INPAINT_NS)
    return result1, result2

优点:对大块污渍(霉斑、水渍边缘、胶带黑影)效果好——它是在"理解周围内容"的基础上补洞,文字笔画方向会被保留。
缺点:必须有一个准确的污点蒙版。蒙版画错(把文字当成污点),inpaint 会"补"出错误的笔画,直接改变档案内容——这是合规大忌。

三、蒙版怎么来:连通域 + 灰度特征

inpainting 成败全在蒙版。我们第一版是人工圈选污点,30 万页显然不现实。自动化蒙版生成方案:

import cv2
import numpy as np

def build_stain_mask(img_gray):
    """生成污点蒙版:深色小连通域(墨滴/虫蛀)"""
    h, w = img_gray.shape
    # 1. 反色二值化,把深色像素变成前景
    _, dark = cv2.threshold(img_gray, 80, 255, cv2.THRESH_BINARY_INV)
    # 2. 连通域分析
    n, labels, stats, _ = cv2.connectedComponentsWithStats(dark, connectivity=8)
    mask = np.zeros_like(img_gray)
    for i in range(1, n):
        area = stats[i, cv2.CC_STAT_AREA]
        bw = stats[i, cv2.CC_STAT_WIDTH]
        bh = stats[i, cv2.CC_STAT_HEIGHT]
        # 污点特征:面积小、尺寸小(不是长文本行)、
        # 且"内部平均灰度"远低于正常文字区域
        if 5 < area < 1500 and bw < 60 and bh < 60:
            # 关键一步:污点内部灰度分布要"实心"
            region = img_gray[labels == i]
            if region.mean() < 50:   # 深色实心,非文字笔画(笔画中间有亮色)
                mask[labels == i] = 255
    return mask

关键技巧:用"实心度"区分文字笔画和污点。文字笔画再粗也是"线条",笔画内部通常有反光或留白;污点是"实心团块",整体灰度均匀且深。判断 region.mean() < 50 且标准差小,能显著降低把文字误判成污点的概率。

02-挂接率验收报表

四、实测对比(老旧人事档案 500 页)

我们挑了一批典型污损档案做对比,指标分"污点清除率"和"文字破坏率"(用 OCR 在修复前后对同一批已知文本做比对,看文字有没有被改):

方案 污点清除率 文字破坏率 单页耗时
中值滤波 5x5 38% 3.5% 90ms
中值滤波 9x9 61% 12% 150ms
高斯滤波 5x5 30% 2.8% 110ms
Telea inpaint 82% 1.2% 450ms
FMM inpaint 79% 1.5% 520ms
组合:小污点用中值 + 大污点用 Telea 91% 0.9% 380ms

(数据来自 500 页实测留底,发布前请用你们自己的样本替换。)

结论非常明确:

  1. 中值滤波窗口不能无脑加大——9x9 时文字破坏率飙到 12%,等于在毁档案;
  2. inpainting 才是去污点的主力,文字破坏率反而更低(因为它理解内容,不会糊笔画);
  3. 组合方案最优:小污点(<5px)用中值滤波快速抹掉,大污点(≥5px)走 inpaint。

五、组合方案的生产实现

def clean_stains(img):
    # 1. 先生成污点蒙版(分大小)
    mask = build_stain_mask(img)
    # 2. 小污点:直接用中值滤波的结果覆盖(蒙版区域)
    med = cv2.medianBlur(img, 5)
    small_mask = cv2.erode(mask, np.ones((3,3), np.uint8))  # 缩一下,只保留小点
    img = np.where(small_mask > 0, med, img)
    # 3. 大污点:inpaint(蒙版区域)
    big_mask = cv2.dilate(mask, np.ones((5,5), np.uint8))   # 扩一下,覆盖边缘
    big_mask = cv2.subtract(big_mask, small_mask)
    img = cv2.inpaint(img, big_mask, 3, cv2.INPAINT_TELEA)
    return img

生产细节:

  • 蒙版必须回退人工复核:我们流水线里每 100 页抽 1 页,把蒙版叠加在图上人工目检,防止自动蒙版误伤文字。抽检不合格批次整体回退重新调参;
  • 水渍处理优先级:水渍往往是浅色大片区域,灰度上接近纸张,build_stain_mask 抓不到。对这类,我们用"局部方差检测"——水渍区域的纹理方差显著低于正常纸面,找到后整片做轻度 inpaint;
  • 合规底线:所有清洗操作只写入"加工层"(展示/OCR 用),原始扫描件永不覆盖。清洗参数和版本号随页记录,可追溯。