python - 字符/数字的边界框检测

标签 python opencv ocr

我有图片,如下所示:

enter image description here

我想找到 8 位数字的边界框。我的第一次尝试是使用带有以下代码的 cv2:

import cv2
import matplotlib.pyplot as plt
import cvlib as cv
from cvlib.object_detection import draw_bbox

im = cv2.imread('31197402.png')
bbox, label, conf = cv.detect_common_objects(im)
output_image = draw_bbox(im, bbox, label, conf)
plt.imshow(output_image)
plt.show()

不幸的是,这不起作用。有人有想法吗?

最佳答案

您的解决方案中的问题很可能是输入图像质量很差。人物和背景之间几乎没有任何对比。 cvlib 中的 Blob 检测算法可能无法区分字符 Blob 和背景,从而生成无用的二进制掩码。让我们尝试使用纯粹的 OpenCV 来解决这个问题。

我建议采取以下步骤:

  1. 应用自适应阈值以获得相当好的二进制掩码。
  2. 使用区域过滤器清除二进制掩码中的 Blob 噪声。
  3. 使用形态学提高二值图像的质量。
  4. 获取每个字符的外部轮廓,并为每个字符 block 拟合一个边界矩形
  5. 裁剪使用先前计算的边界矩形的每个字符。

让我们看一下代码:

# importing cv2 & numpy:
import numpy as np
import cv2

# Set image path
path = "C:/opencvImages/"
fileName = "mrrm9.png"

# Read input image:
inputImage = cv2.imread(path+fileName)
inputCopy = inputImage.copy()

# Convert BGR to grayscale:
grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

从这里开始没什么可讨论的,只是读取BGR图像并将其转换为灰度。现在,让我们使用 gaussian 方法应用一个自适应阈值。这是棘手的部分,因为参数是根据输入质量手动调整的。该方法的工作方式是将图像划分为 windowSize 的单元格网格,然后应用局部阈值来找到前景和背景之间的最佳分离。可以将一个由 windowConstant 指示的附加常量添加到阈值以微调输出:

# Set the adaptive thresholding (gasussian) parameters:
windowSize = 31
windowConstant = -1
# Apply the threshold:
binaryImage = cv2.adaptiveThreshold(grayscaleImage, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, windowSize, windowConstant)

你得到了这个漂亮的二值图像:

现在,如您所见,图像有一些 Blob 噪声。让我们应用一个 area filter 来消除噪音。噪音小于感兴趣的目标 Blob ,因此我们可以根据面积轻松过滤它们,如下所示:

# Perform an area filter on the binary blobs:
componentsNumber, labeledImage, componentStats, componentCentroids = \
cv2.connectedComponentsWithStats(binaryImage, connectivity=4)

# Set the minimum pixels for the area filter:
minArea = 20

# Get the indices/labels of the remaining components based on the area stat
# (skip the background component at index 0)
remainingComponentLabels = [i for i in range(1, componentsNumber) if componentStats[i][4] >= minArea]

# Filter the labeled pixels based on the remaining labels,
# assign pixel intensity to 255 (uint8) for the remaining pixels
filteredImage = np.where(np.isin(labeledImage, remainingComponentLabels) == True, 255, 0).astype('uint8')

这是过滤后的图像:

我们可以通过一些形态学来提高这张图片的质量。一些字符似乎被破坏了(查看第一个 3 - 它被破坏成两个分开的 blob)。我们可以加入他们应用关闭操作:

# Set kernel (structuring element) size:
kernelSize = 3

# Set operation iterations:
opIterations = 1

# Get the structuring element:
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))

# Perform closing:
closingImage = cv2.morphologyEx(filteredImage, cv2.MORPH_CLOSE, maxKernel, None, None, opIterations, cv2.BORDER_REFLECT101)

这是“关闭”图像:

现在,您想要获取每个字符的边界框。让我们检测每个 blob 的外部轮廓并在其周围放置一个漂亮的矩形:

# Get each bounding box
# Find the big contours/blobs on the filtered image:
contours, hierarchy = cv2.findContours(closingImage, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

contours_poly = [None] * len(contours)
# The Bounding Rectangles will be stored here:
boundRect = []

# Alright, just look for the outer bounding boxes:
for i, c in enumerate(contours):

    if hierarchy[0][i][3] == -1:
        contours_poly[i] = cv2.approxPolyDP(c, 3, True)
        boundRect.append(cv2.boundingRect(contours_poly[i]))


# Draw the bounding boxes on the (copied) input image:
for i in range(len(boundRect)):
    color = (0, 255, 0)
    cv2.rectangle(inputCopy, (int(boundRect[i][0]), int(boundRect[i][1])), \
              (int(boundRect[i][0] + boundRect[i][2]), int(boundRect[i][1] + boundRect[i][3])), color, 2)

最后一个 for 循环几乎是可选的。它从列表中获取每个边界矩形并将其绘制在输入图像上,因此您可以看到每个单独的矩形,如下所示:

让我们在二值图像上可视化:

另外,如果你想使用我们刚刚得到的边界框裁剪每个字符,你可以这样做:

# Crop the characters:
for i in range(len(boundRect)):
    # Get the roi for each bounding rectangle:
    x, y, w, h = boundRect[i]

    # Crop the roi:
    croppedImg = closingImage[y:y + h, x:x + w]
    cv2.imshow("Cropped Character: "+str(i), croppedImg)
    cv2.waitKey(0)

这就是您获取各个边界框的方法。现在,也许您正在尝试将这些图像传递给 OCR。我尝试将过滤后的二进制图像(在关闭操作之后)传递给pyocr(这是我正在使用的 OCR),我将其作为输出字符串:31197402

我用来获取关闭图像的OCR的代码是这样的:

# Set the OCR libraries:
from PIL import Image
import pyocr
import pyocr.builders

# Set pyocr tools:
tools = pyocr.get_available_tools()
# The tools are returned in the recommended order of usage
tool = tools[0]

# Set OCR language:
langs = tool.get_available_languages()
lang = langs[0]

# Get string from image:
txt = tool.image_to_string(
    Image.open(path + "closingImage.png"),
    lang=lang,
    builder=pyocr.builders.TextBuilder()
)

print("Text is:"+txt)

请注意,OCR 接收白底黑字,因此您必须先反转图像。

关于python - 字符/数字的边界框检测,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65738928/

相关文章:

python - 在 Python 中将每月数据转换为每日数据

python - 霍夫圆变换为圆形阴影

c# - tesseract (.NET) + 可搜索的 PDF。如何申请?

c# - 需要 Java 或 .NET 中的阿拉伯语 OCR 开源

python - 在 OpenGL 中围绕中心点旋转四边形

python - 使用 Python OpenCV 将二维码旋转到正确的位置

c++ - 获取存储在矩阵中的图像的 ROI 的平均 R、G 和 B

图像预处理 : contour expansion

ocr - Tesseract 多种输出格式

python - Hive自定义脚本是否允许2个或更多的reducer?