For OCR | 基于 OpenCV 的图像预处理

欢迎关注 “小白玩转Python”,发现更多 “有趣”

如果你的图像有随机噪声,不均匀的光照,前面的物体上的洞,等等。在将图片发布到计算机视觉 API 之前,有几件事情是你可以做的。在本文中,我们将介绍几种使用 OpenCV 提高 OCR 处理效果的技术。

安装 OpenCV

使用首选的包管理器安装 OpenCV

conda install -c conda-forge opencv
pip install opencv-python

读取图片

我将以我最喜欢的一本书的第一版的封面为例。让我们首先读取图像,指定要读取图像的颜色类型,这将读取图像的默认颜色格式为 OpenCV 中的 BGR (即蓝绿红)。然后我们将颜色空间转换为更常见的 RGB 顺序(为了可视化) ,最后,编写一个小函数来显示图像:

import cv2
import matplotlib.pyplot as plt


img = cv2.imread(
  filename='hands-on-machine-learning.jpg',
  flags=cv2.IMREAD_COLOR,
)


h, w, c = img.shape
print(f'Image shape: {h}H x {w}W x {c}C')


img = cv2.cvtColor(
  src=img,    
  code=cv2.COLOR_BGR2RGB,
)




def show_image(img, **kwargs):
    """
    Show an RGB numpy array of an image without any interpolation
    """
    plt.subplot()
    plt.axis('off')
    plt.imshow(
        X=img,
        interpolation='none',
        **kwargs
    )
    
    
show_image(img)

裁剪图像

大多数情况下,您要么在文本周围有一个框坐标(来自标签工具) ,要么您只对图像的一部分感兴趣。我们的图像是一个 3D numpy 数组。要裁剪它,我们可以简单地沿着高度和宽度切片:

ymin, ymax = 200, 780
xmin, xmax = 100, 1000


img = img[
    int(ymin): int(ymax),
    int(xmin): int(xmax),
]


h, w, c = img.shape
print(f'Image shape: {h}H x {w}W x {c}C')


show_image(img)

扩充边界

这对于使用针对文档进行 OCR 模型训练的 API (文档通常有白色边框)可能很有用

img_border = cv2.copyMakeBorder(
    src=img,
    top=10,
    bottom=10,
    left=10,
    right=10,
    borderType=cv2.BORDER_CONSTANT,
    value=(255, 255, 255),
)


h, w, c = img_border.shape
print(f'Image shape: {h}H x {w}W x {c}C')


show_image(img_border)

调整图像大小

API 将为输入图像设置一个最大尺寸。如果图像需要调整大小,应该保留长宽比

MAX_PIX = 800




def resize_image(img, flag):
    """
    Resize an RGB numpy array of an image, either along the height or the width, and keep its aspect ratio. Show restult.
    """
    h, w, c = img.shape


    if flag == 'h':
        dsize = (int((MAX_PIX * w) / h), int(MAX_PIX))
    else:
        dsize = (int(MAX_PIX), int((MAX_PIX * h) / w))


    img_resized = cv2.resize(
        src=img,
        dsize=dsize,
        interpolation=cv2.INTER_CUBIC,
    )


    h, w, c = img_resized.shape
    print(f'Image shape: {h}H x {w}W x {c}C')
    
    show_image(img_resized)


    return img_resized




if h > MAX_PIX:
    img_resized = resize_image(img, 'h')    


if w > MAX_PIX:
    img_resized = resize_image(img, 'w') 

图像形态学操作

  • Opening:腐蚀后使用 5×5 的内核膨胀操作,有助于消除噪声

  • Closing:膨胀后使用 5×5 的内核腐蚀操作,以用来堵住小洞

def apply_morphology(img, method):
    """
    Apply a morphological operation, either opening (i.e. erosion followed by dilation) or closing (i.e. dilation followed by erosion). Show result.
    """
    if method == 'open':
        op = cv2.MORPH_OPEN
    elif method == 'close':
        op = cv2.MORPH_CLOSE


    img_morphology = cv2.morphologyEx(
        src=img,
        op=op,
        kernel=np.ones((5, 5), np.uint8),
    )


    show_image(img_morphology)


    return img_morphology

高斯模糊操作

图像模糊(又名平滑)是有用的消除高频(如噪声)的操作。注意,内核越大,模糊效果越差。

img_gaussian = cv2.GaussianBlur(
    src=img,
    ksize=(5, 5),
    sigmaX=0,
    sigmaY=0,
)

自适应阈值操作

阈值化将灰度图像转换为二值图像。自适应阈值是有用的时候,图像有不同的照明条件在不同的地区,因为它计算不同的阈值不同的区域。

下面的函数使用以下两种方法中的一种来自适应阈值化:

  • 高斯:阈值是邻域值的加权和,其中权重是一个高斯窗口

  • 平均值:阈值是邻域面积的平均值

def apply_adaptive_threshold(img, method):
    """
    Apply adaptive thresholding, either Gaussian (threshold value is the weighted sum of neighbourhood values where weights are a Gaussian window) or mean (threshold value is the mean of neighbourhood area). Show result.
    """
    img = cv2.cvtColor(
        src=img,
        code=cv2.COLOR_RGB2GRAY,
    )


    if method == 'gaussian':
        adaptive_method = cv2.ADAPTIVE_THRESH_GAUSSIAN_C
    elif method == 'mean':
        adaptive_method = cv2.ADAPTIVE_THRESH_MEAN_C


    img_adaptive = cv2.adaptiveThreshold(
        src=img,
        maxValue=255,
        adaptiveMethod=adaptive_method,
        thresholdType=cv2.THRESH_BINARY,
        blockSize=11,
        C=2,
    )


    show_image(img_adaptive, cmap='gray')


    return img_adaptive

Sobel 滤波器操作

Sobel 算子结合了高斯平滑和微分(图像沿 x 或 y 的一阶导数)。它们有助于检测水平或垂直边缘,并能抵抗噪声。为了检测水平和垂直边缘,我们可以沿着 x 和 y 分别使用 Sobel 滤波器操作,然后查看结果。

def apply_sobel(img, direction):


    img = cv2.cvtColor(
        src=img,
        code=cv2.COLOR_RGB2GRAY,
    )


    if direction == 'h':
        dx, dy = 0, 1


    elif direction == 'v':
        dx, dy = 1, 0


    img_sobel = cv2.Sobel(
        src=img,
        ddepth=cv2.CV_64F,
        dx=dx,
        dy=dy,
        ksize=5,
    )


    return img_sobel

拉普拉斯滤波器操作

与 Sobel 算子类似,拉普拉斯算子使用微分进行处理。但是,他们使用图像沿 x 和 y 的二阶导数(通过内部加上使用 Sobel 算子计算的二阶 x 和 y 导数)。拉普拉斯算子用于检测边缘(以及其他二阶导数为0的可能无意义的位置,所以它们应该只适用于需要的地方)。

def apply_laplacian(img):


    img = cv2.cvtColor(
        src=img,
        code=cv2.COLOR_RGB2GRAY,
    )


    img_laplacian = np.uint8(
        np.absolute(
            cv2.Laplacian(
                src=img,
                ddepth=cv2.CV_64F,
            )
        )
    )


    show_image(img_laplacian, cmap='gray')


    return img_laplacian

其他代码

最后,当你的图像中有一些不同区域的框坐标时(例如,你使用了一个标签工具来标注标题、副标题、作者等) ,你不希望: 裁剪每个区域 > 保存裁剪后的图像本地 > 使用本地图像发布一个 API 请求,因为这会拖慢你的速度。

为了解决这个问题,您可以改为:裁剪每个区域 > 编码裁剪图像到内存缓冲区 > 后请求使用内存缓冲区中的图像,这比第一种方法更快。

下面展示了如何将图像编码到 buffer 中,然后使用 buffer 为 OCR 请求准备好数据(以及如果需要的话如何将图像解码回来) :

_, buf = cv2.imencode(
    ext=".jpg",
    img=img,
)


data = buf.tostring()


img = cv2.imdecode(
    buf=buf,
    flags=cv2.IMREAD_UNCHANGED,
)

·  END  ·

HAPPY LIFE

### 如何修复OpenCV图像预处理中的常见问题和缺陷 #### 图像二值化调整 在进行OCR或其他基于阈值的操作时,合适的二值化至关重要。如果发现二值化的结果不佳,可以通过调整阈值参数或尝试不同的自适应阈值方法来改善效果[^5]。 ```python import cv2 # 自定义全局阈值法 ret, thresh_global = cv2.threshold(gray_image, 127, 255, cv2.THRESH_BINARY) # 使用Otsu's二值化自动计算最佳阈值 ret_otsu, thresh_otsu = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) ``` #### 去除噪声优化 为了提高图像质量并减少误检的可能性,在预处理阶段去除不必要的细节非常重要。除了开运算外,还可以考虑应用中值滤波器以更好地保留边缘特征而不引入新的伪影[^4]。 ```python # 应用中值模糊移除椒盐噪声的同时保持边界清晰度 median_blur_result = cv2.medianBlur(gray_image, ksize=5) ``` #### 形态学操作改进 当面对特定类型的结构干扰物(如细小颗粒),适当调节形态学变换所使用的内核大小能够有效提升清理效率。对于不同尺度的对象,可能需要测试多种尺寸的矩形、椭圆或者其他形状的内核[^2]。 ```python kernel_rect = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5)) cleaned_img = cv2.morphologyEx(median_blur_result, cv2.MORPH_OPEN, kernel_rect) ``` #### 文本区域矫正 针对倾斜的文字排列情况,先利用霍夫变换找到文档轮廓的角度信息再做旋转校准有助于后续文字提取工作的准确性。 ```python def correct_skew(image): coords = np.column_stack(np.where(thresh > 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) return rotated ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值