档案数字化加工平台,实现数字化加工流程化管理,扫描,批量修图,ocr著录,流程控制。

最近在折腾档案数字化平台,发现流程化管理真是个技术活。想象一下要把堆积如山的纸质档案变成可检索的电子数据,光靠扫描仪可不够——得把图像处理、文字识别、流程监控这些活串成自动化流水线才靠谱。

先说说扫描模块的坑。很多单位用的扫描仪型号五花八门,我们直接上PyWin32调用Windows图像采集接口:

import win32com.client
wia = win32com.client.Dispatch("WIA.CommonDialog")
device = wia.ShowSelectDevice()
item = device.Items[1]
image = item.Transfer()
image.SaveFile("D:/scan_img.jpg")

这段代码看着简单,实际调试时发现不同品牌的扫描仪返回的Items索引可能不一样。后来加了个设备类型检测逻辑,遇到富士通扫描仪自动切换Items[2],才算搞定兼容问题。

批量修图才是真酸爽。档案经常有黑边、倾斜、折痕,用OpenCV搞了个自适应清理流程:

def auto_fix(img_path):
    img = cv2.imread(img_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    mask = cv2.inRange(gray, 0, 15)
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    x,y,w,h = cv2.boundingRect(max(contours, key=cv2.contourArea))
    # 裁切后做角度校正
    cropped = img[y:y+h, x:x+w]
    angle = detect_skew(cropped)  # 自定义的倾斜检测函数
    rotated = rotate_image(cropped, angle)
    return denoise(rotated)  # 非局部均值去噪

有个冷知识:档案纸张的折痕在灰度图上比彩色图明显得多,转HSV空间处理反而容易误伤正文内容。

档案数字化加工平台,实现数字化加工流程化管理,扫描,批量修图,ocr著录,流程控制。

OCR著录环节最头疼的是表格识别。试了PaddleOCR和Tesseract,最后发现混合方案效果最佳:

from paddleocr import PaddleOCR
import pytesseract

ocr_engine = PaddleOCR(use_angle_cls=True)

def hybrid_ocr(img):
    # 先用Paddle检测表格区域
    table_boxes = detect_tables(img)
    for box in table_boxes:
        crop_img = crop_with_perspective(img, box)
        # 表格内用Tesseract保持格式
        table_text = pytesseract.image_to_string(crop_img, config='--psm 6')
        yield table_text
    # 非表格区域用PaddleOCR
    result = ocr_engine.ocr(img)
    yield from (line[1][0] for line in result)

实测发现Tesseract在规整表格的数字识别准确率比Paddle高8%左右,但标题类文字反而容易漏检。这种缝合怪方案虽然不优雅,但准确率能到96%以上。

流程控制用了Celery+Redis做任务队列,关键是要处理扫描仪这类物理设备的并发冲突。给每个硬件设备加了状态锁:

from celery import Celery
from redis import Redis

app = Celery('tasks', broker='redis://localhost/0')
redis = Redis()

@app.task
def scan_task(device_id):
    if redis.setnx(f"scanner:{device_id}_lock", 1):
        redis.expire(f"scanner:{device_id}_lock", 300)
        try:
            # 执行扫描操作
            return do_scan(device_id)
        finally:
            redis.delete(f"scanner:{device_id}_lock")
    else:
        raise Exception("扫描仪被占用")

遇到过死锁问题——某任务崩溃没释放锁,后来加了expire自动过期,再配合看门狗线程定期检查,才算稳住生产线。

搞数字化平台就像在流水线上跳舞,既要保证每个环节的精度,又得让数据像传送带上的零件一样顺畅流转。有时候解决一个图像纠偏的算法问题,比处理十台罢工的扫描仪还有成就感——至少代码不会因为电压不稳闹脾气不是?

更多推荐