【OCR学习笔记】:OCR 常用图像预处理方法

OCR 常用图像预处理方法

在 OCR 项目中,很多人一开始会把重点放在模型选择上,比如 PaddleOCR、Tesseract、EasyOCR、RapidOCR 等。但实际开发中经常会发现:同一个 OCR 模型,在不同图片上的识别效果差异非常大。

原因往往不是模型不行,而是图片质量没有处理好。

OCR 的本质是让模型从图片中识别文字,所以图片越"干净"、文字越"清晰"、背景干扰越少,识别准确率就越高。本文就来系统讲解 OCR 中常用的图片处理方法。


一、为什么 OCR 前要处理图片?

OCR 模型虽然有一定的鲁棒性,但它并不是万能的。下面这些情况都会明显影响识别效果:

  • 图片太暗或太亮
  • 文字和背景颜色接近
  • 图片模糊
  • 文字太小
  • 截图中有大量无关区域
  • 背景纹理复杂
  • 图片倾斜
  • 文字被边框、图标、阴影干扰
  • 压缩严重,出现噪点

所以 OCR 前通常会先做一轮图像预处理,让图片更适合识别。

一个常见流程是:

text 复制代码
原图
  ↓
裁剪目标区域
  ↓
缩放放大
  ↓
灰度化
  ↓
增强对比度
  ↓
二值化
  ↓
降噪
  ↓
OCR识别
  ↓
结果纠错

并不是所有场景都要完整执行这些步骤,而是根据图片情况选择合适的方法。


二、区域裁剪:先把无关内容去掉

OCR 最重要的一步往往不是增强图片,而是裁剪识别区域

如果整张截图里只有一小块文字需要识别,却直接把全图送进 OCR,模型会受到很多干扰。

例如游戏截图、App 页面、表格截图中,文字通常只出现在固定位置。这个时候可以先根据坐标裁剪:

python 复制代码
import cv2

image = cv2.imread("screen.png")

# 裁剪区域:y1:y2, x1:x2
crop = image[100:300, 50:500]

cv2.imwrite("crop.png", crop)

在 OCR 项目中,推荐优先做区域裁剪。

因为裁剪可以:

  1. 减少干扰内容
  2. 提高识别速度
  3. 提高识别准确率
  4. 方便后续单独调参

例如识别"落款名""金额""地址信息""日期"时,最好不要整图识别,而是分别裁剪每个字段对应的区域。


三、图片缩放:小字识别前先放大

很多 OCR 识别失败,是因为文字太小。

尤其是手机截图、游戏界面、网页缩略图,文字区域可能只有十几像素高。这个时候可以先放大图片。

python 复制代码
import cv2

image = cv2.imread("crop.png")

# 放大 2 倍
resized = cv2.resize(image, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)

cv2.imwrite("resized.png", resized)

常用插值方式:

python 复制代码
cv2.INTER_LINEAR   # 普通缩放
cv2.INTER_CUBIC    # 放大效果较好
cv2.INTER_AREA     # 缩小时常用

一般建议:

text 复制代码
文字较小:放大 2~4 倍
文字正常:不放大或放大 1.5~2 倍
图片已经很大:不要盲目放大

放大不是越大越好。放大过度会让边缘变虚,反而影响 OCR。


四、灰度化:减少颜色干扰

OCR 识别文字时,很多时候并不需要颜色信息。把彩色图转成灰度图,可以减少干扰,也方便后续二值化、降噪、阈值处理。

python 复制代码
import cv2

image = cv2.imread("crop.png")

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

cv2.imwrite("gray.png", gray)

灰度化适合这些场景:

  • 黑字白底
  • 白字黑底
  • 文档扫描件
  • 表格截图
  • 简单背景文字

如果文字颜色本身很关键,比如游戏里的金色、红色、紫色品质文字,有时候保留彩色再做颜色提取会更好。


五、对比度增强:让文字和背景拉开差距

如果文字和背景颜色太接近,OCR 很容易识别错。这个时候可以增强对比度。

1. 普通对比度增强

python 复制代码
import cv2

image = cv2.imread("gray.png", 0)

# alpha 控制对比度,beta 控制亮度
enhanced = cv2.convertScaleAbs(image, alpha=1.5, beta=0)

cv2.imwrite("enhanced.png", enhanced)

参数说明:

text 复制代码
alpha > 1:增强对比度
beta > 0:提高亮度
beta < 0:降低亮度

例如:

python 复制代码
enhanced = cv2.convertScaleAbs(image, alpha=1.8, beta=10)

适合偏暗、文字不清晰的图片。


2. CLAHE 局部对比度增强

如果图片局部亮度不均,比如一边亮一边暗,可以使用 CLAHE。

python 复制代码
import cv2

gray = cv2.imread("gray.png", 0)

clahe = cv2.createCLAHE(
    clipLimit=2.0,
    tileGridSize=(8, 8)
)

result = clahe.apply(gray)

cv2.imwrite("clahe.png", result)

CLAHE 比普通对比度增强更适合:

  • 扫描件
  • 拍照文档
  • 光照不均的图片
  • 背景有阴影的图片

六、二值化:把文字和背景分开

二值化就是把图片变成黑白两种颜色,让文字和背景更分明。

1. 固定阈值二值化

python 复制代码
import cv2

gray = cv2.imread("gray.png", 0)

_, binary = cv2.threshold(
    gray,
    127,
    255,
    cv2.THRESH_BINARY
)

cv2.imwrite("binary.png", binary)

意思是:

text 复制代码
像素值 > 127 的变成白色
像素值 <= 127 的变成黑色

适合光照均匀、背景简单的图片。


2. 反向二值化

如果是白字黑底,可以用反向二值化:

python 复制代码
_, binary_inv = cv2.threshold(
    gray,
    127,
    255,
    cv2.THRESH_BINARY_INV
)

常见场景:

text 复制代码
黑字白底:THRESH_BINARY
白字黑底:THRESH_BINARY_INV

3. Otsu 自动阈值

固定阈值不一定适合所有图片,Otsu 可以自动寻找一个较合适的阈值。

python 复制代码
_, otsu = cv2.threshold(
    gray,
    0,
    255,
    cv2.THRESH_BINARY + cv2.THRESH_OTSU
)

cv2.imwrite("otsu.png", otsu)

适合:

  • 背景较简单
  • 文字和背景有明显灰度差
  • 不想手动调阈值

4. 自适应阈值

如果图片光照不均,固定阈值和 Otsu 可能效果不好。可以使用自适应阈值。

python 复制代码
adaptive = cv2.adaptiveThreshold(
    gray,
    255,
    cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv2.THRESH_BINARY,
    31,
    10
)

cv2.imwrite("adaptive.png", adaptive)

参数说明:

text 复制代码
31:局部区域大小,必须是奇数
10:常数偏移量

适合:

  • 拍照图片
  • 阴影明显
  • 背景亮度不均
  • 纸张泛黄
  • 局部过曝或过暗

七、降噪:去掉背景杂点

图片中如果有很多噪点,会影响 OCR 判断文字边缘。

1. 中值滤波

中值滤波常用于去除椒盐噪声。

python 复制代码
denoise = cv2.medianBlur(gray, 3)

cv2.imwrite("denoise.png", denoise)

适合:

  • 图片有小黑点
  • 扫描件有杂点
  • 背景不干净

2. 高斯模糊

高斯模糊可以平滑图像,但要谨慎使用,因为它可能让文字边缘变虚。

python 复制代码
blur = cv2.GaussianBlur(gray, (3, 3), 0)

cv2.imwrite("blur.png", blur)

适合轻微噪声,不适合文字本来就很细、很小的图片。


3. 非局部均值降噪

python 复制代码
denoise = cv2.fastNlMeansDenoising(
    gray,
    None,
    h=10,
    templateWindowSize=7,
    searchWindowSize=21
)

cv2.imwrite("nlmeans.png", denoise)

这种方式效果较好,但速度相对慢一些。


八、锐化:让文字边缘更清楚

如果图片有点模糊,可以适当锐化。

python 复制代码
import cv2
import numpy as np

image = cv2.imread("gray.png", 0)

kernel = np.array([
    [0, -1, 0],
    [-1, 5, -1],
    [0, -1, 0]
])

sharp = cv2.filter2D(image, -1, kernel)

cv2.imwrite("sharp.png", sharp)

锐化适合:

  • 文字边缘发虚
  • 截图压缩后不清晰
  • 轻微模糊

但锐化不能过度,否则会产生明显噪点,让 OCR 更难识别。


九、形态学处理:修复断裂文字或去除小干扰

形态学操作主要包括腐蚀、膨胀、开运算、闭运算。

1. 膨胀:让文字变粗

python 复制代码
kernel = np.ones((2, 2), np.uint8)

dilate = cv2.dilate(binary, kernel, iterations=1)

cv2.imwrite("dilate.png", dilate)

适合文字太细、断裂的情况。


2. 腐蚀:让文字变细,去掉小噪点

python 复制代码
erode = cv2.erode(binary, kernel, iterations=1)

cv2.imwrite("erode.png", erode)

适合文字过粗、粘连的情况。


3. 开运算:先腐蚀再膨胀,去小噪点

python 复制代码
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)

cv2.imwrite("opening.png", opening)

适合去除独立小噪点。


4. 闭运算:先膨胀再腐蚀,连接断裂文字

python 复制代码
closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)

cv2.imwrite("closing.png", closing)

适合文字笔画断裂、边缘不连续的情况。


十、倾斜校正:让文字保持水平

如果图片是拍照得到的,文字可能会倾斜。倾斜会影响 OCR 的检测和识别。

常见做法是:

  1. 找到文字区域轮廓
  2. 计算倾斜角度
  3. 旋转图片校正

简单示例:

python 复制代码
import cv2
import numpy as np

image = cv2.imread("text.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

_, binary = cv2.threshold(
    gray,
    0,
    255,
    cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU
)

coords = np.column_stack(np.where(binary > 0))
angle = cv2.minAreaRect(coords)[-1]

if angle < -45:
    angle = -(90 + angle)
else:
    angle = -angle

h, w = image.shape[:2]
center = (w // 2, h // 2)

M = cv2.getRotationMatrix2D(center, angle, 1.0)

rotated = cv2.warpAffine(
    image,
    M,
    (w, h),
    flags=cv2.INTER_CUBIC,
    borderMode=cv2.BORDER_REPLICATE
)

cv2.imwrite("rotated.png", rotated)

倾斜校正常用于:

  • 发票
  • 合同
  • 票据
  • 拍照文档
  • 扫描件

十一、颜色过滤:提取指定颜色文字

在游戏、App、海报类图片中,文字可能不是黑白的,而是彩色文字。

例如:

  • 金色文字
  • 红色文字
  • 蓝色文字
  • 白色描边文字
  • 紫色品质文字

这种时候直接灰度化可能丢失关键信息,可以用 HSV 颜色空间提取目标颜色。

python 复制代码
import cv2
import numpy as np

image = cv2.imread("screen.png")

hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# 示例:提取黄色/金色区域
lower = np.array([15, 80, 80])
upper = np.array([40, 255, 255])

mask = cv2.inRange(hsv, lower, upper)

result = cv2.bitwise_and(image, image, mask=mask)

cv2.imwrite("color_text.png", result)

颜色过滤适合:

  • 游戏资源识别
  • 等级、品质、数量识别
  • 固定颜色标签识别
  • UI 截图 OCR

但要注意,不同设备、压缩、亮度会导致颜色范围变化,所以 HSV 阈值需要调试。


十二、去边框和去图标干扰

有些 OCR 区域里会包含边框、图标、按钮、装饰线,这些都会影响识别。

常见处理方式:

1. 更精准裁剪

这是最推荐的方式。能裁掉干扰,就不要交给后处理。

python 复制代码
crop = image[y1:y2, x1:x2]

2. 根据颜色去除背景

如果边框颜色固定,可以通过颜色过滤去掉。

3. 根据形态学去除线条

比如表格线、横线、竖线可以用形态学检测后去除。

python 复制代码
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40, 1))
remove_horizontal = cv2.morphologyEx(binary, cv2.MORPH_OPEN, horizontal_kernel)

4. 使用掩码

如果某些干扰位置固定,可以直接把那块区域涂白或涂黑。

python 复制代码
image[y1:y2, x1:x2] = 255

这个方法在固定 UI 截图中特别实用。


十三、一个通用 OCR 预处理函数示例

下面是一个比较通用的处理函数,适合截图类 OCR:

python 复制代码
import cv2
import numpy as np


def preprocess_for_ocr(image_path: str, output_path: str = "processed.png"):
    image = cv2.imread(image_path)

    if image is None:
        raise ValueError(f"图片读取失败: {image_path}")

    # 1. 放大
    image = cv2.resize(
        image,
        None,
        fx=2,
        fy=2,
        interpolation=cv2.INTER_CUBIC
    )

    # 2. 灰度化
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 3. 对比度增强
    enhanced = cv2.convertScaleAbs(gray, alpha=1.5, beta=5)

    # 4. 轻微降噪
    denoise = cv2.medianBlur(enhanced, 3)

    # 5. Otsu 二值化
    _, binary = cv2.threshold(
        denoise,
        0,
        255,
        cv2.THRESH_BINARY + cv2.THRESH_OTSU
    )

    cv2.imwrite(output_path, binary)

    return binary

调用方式:

python 复制代码
processed = preprocess_for_ocr("input.png", "processed.png")

十四、总结

OCR 图片处理的核心目标只有一个:让文字更突出,让干扰更少。

常用方法可以总结为:

text 复制代码
裁剪:减少无关区域
缩放:解决小字问题
灰度化:减少颜色干扰
对比度增强:拉开文字和背景差距
二值化:突出文字轮廓
降噪:去除背景杂点
锐化:增强文字边缘
形态学:修复断裂或去除干扰
倾斜校正:处理拍照文档
颜色过滤:处理彩色文字
后处理:修正 OCR 结果

OCR 的准确率提升,通常不是靠单一方法,而是靠一整套稳定的图像处理流程。只要把图片处理好,即使使用普通 OCR 模型,也能得到不错的识别效果。