使用 cv2 / pytesseract 增强数字识别的局部对比度

spa*_*del 2 python ocr opencv python-tesseract

我想使用 pytesseract 从图像中读取数字。图像如下:

在此输入图像描述

在此输入图像描述

数字是点状的,为了能够使用 pytesseract,我需要白色背景上的黑色连接数字。为此,我考虑使用侵蚀扩张作为预处理技术。正如您所看到的,这些图像很相似,但在某些方面却截然不同。例如,第一幅图像中的点比背景更暗,而第二幅图像中的点比背景更白。这意味着,在第一张图像中,我可以使用侵蚀来获得黑色连接线,在第二张图像中,我可以使用扩张来获得白色连接线,然后反转颜色。这导致以下结果:

在此输入图像描述

在此输入图像描述

使用适当的阈值,可以使用 pytesseract 轻松读取第一张图像。第二张图片,不管是谁,都比较棘手。问题是,例如“4”的某些部分比“3”周围的背景更暗。所以简单的阈值是行不通的。我需要诸如局部阈值或局部对比度增强之类的东西。这里有人有想法吗?

编辑:

OTSU、平均阈值和高斯阈值导致以下结果:

在此输入图像描述

sta*_*ine 5

您的图像分辨率相当低,但您可以尝试一种称为“增益除法”的方法。这个想法是,您尝试构建背景模型,然后根据该模型对每个输入像素进行加权。在图像的大部分时间里,输出增益应该相对恒定。

\n

执行增益划分后,您可以尝试通过应用区域滤波器形态学来改善图像。我只尝试了你的第一张图片,因为它是“最糟糕的”。

\n

这些是获得增益分割图像的步骤:

\n
    \n
  1. 应用软中值模糊滤波器来消除高频噪声。
  2. \n
  3. 通过局部最大值得到背景的模型。应用一个非常强的close运算,具有很大的值structuring element(I\xe2\x80\x99m 使用大小为 的矩形内核15)。
  4. \n
  5. 通过在每个局部最大像素之间划分来执行增益调整255。使用每个输入图像像素对该值进行加权。
  6. \n
  7. 您应该获得一个漂亮的图像,其中背景照明几乎已标准化threshold该图像可以获得字符的二进制蒙版。
  8. \n
\n

现在,您可以通过以下附加步骤来提高图像质量:

\n
    \n
  1. Threshold通过大津,但添加一点点偏见。(不幸的是,这是一个手动步骤,具体取决于输入)。

    \n
  2. \n
  3. 应用区域过滤器过滤掉较小的噪声斑点。

    \n
  4. \n
\n

我们看一下代码:

\n
import numpy as np\nimport cv2\n\n# image path\npath = "C:/opencvImages/"\nfileName = "iA904.png"\n\n# Reading an image in default mode:\ninputImage = cv2.imread(path+fileName)\n\n# Remove small noise via median:\nfilterSize = 5\nimageMedian = cv2.medianBlur(inputImage, filterSize)\n\n# Get local maximum:\nkernelSize = 15\nmaxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))\nlocalMax = cv2.morphologyEx(imageMedian, cv2.MORPH_CLOSE, maxKernel, None, None, 1, cv2.BORDER_REFLECT101)\n\n# Perform gain division\ngainDivision = np.where(localMax == 0, 0, (inputImage/localMax))\n\n# Clip the values to [0,255]\ngainDivision = np.clip((255 * gainDivision), 0, 255)\n\n# Convert the mat type from float to uint8:\ngainDivision = gainDivision.astype("uint8") \n\n# Convert RGB to grayscale:\ngrayscaleImage = cv2.cvtColor(gainDivision, cv2.COLOR_BGR2GRAY)\n
Run Code Online (Sandbox Code Playgroud)\n

这就是增益除法给你带来的结果:

\n\n

请注意,照明更加平衡。现在,让我们应用一点对比度增强:

\n
# Contrast Enhancement:\ngrayscaleImage = np.uint8(cv2.normalize(grayscaleImage, grayscaleImage, 0, 255, cv2.NORM_MINMAX))\n
Run Code Online (Sandbox Code Playgroud)\n

你会得到这样的结果,这会在前景和背景之间产生更多的对比度:

\n\n

现在,让我们尝试对该图像进行阈值处理以获得一个漂亮的二元掩模。正如我所建议的,尝试大津的阈值处理,但在结果中添加(或减去)一点偏差。如前所述,此步骤取决于您输入的质量:

\n
# Threshold via Otsu + bias adjustment:\nthreshValue, binaryImage = cv2.threshold(grayscaleImage, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n\nthreshValue = 0.9 * threshValue\n_, binaryImage = cv2.threshold(grayscaleImage, threshValue, 255, cv2.THRESH_BINARY)\n
Run Code Online (Sandbox Code Playgroud)\n

你最终得到这个二进制掩码:

\n\n

反转它并过滤掉小斑点。我设置了像素area的阈值10

\n
# Invert image:\nbinaryImage = 255 - binaryImage\n\n# Perform an area filter on the binary blobs:\ncomponentsNumber, labeledImage, componentStats, componentCentroids = \\\ncv2.connectedComponentsWithStats(binaryImage, connectivity=4)\n\n# Set the minimum pixels for the area filter:\nminArea = 10\n\n# Get the indices/labels of the remaining components based on the area stat\n# (skip the background component at index 0)\nremainingComponentLabels = [i for i in range(1, componentsNumber) if componentStats[i][4] >= minArea]\n\n# Filter the labeled pixels based on the remaining labels,\n# assign pixel intensity to 255 (uint8) for the remaining pixels\nfilteredImage = np.where(np.isin(labeledImage, remainingComponentLabels) == True, 255, 0).astype("uint8")\n
Run Code Online (Sandbox Code Playgroud)\n

这是最终的二进制掩码:

\n\n

如果您打算将此图像发送到OCR,您可能需要首先应用一些形态学。也许是closing尝试将构成角色的点连接起来。还要确保使用接近您实际想要识别的OCR字体来训练您的分类器。这是经过迭代的大小操作后的(反转)掩码:3 rectangular closing3

\n\n

编辑:

\n

要获取最后一个图像,请按如下方式处理过滤后的输出:

\n
# Set kernel (structuring element) size:\nkernelSize = 3\n\n# Set operation iterations:\nopIterations = 3\n\n# Get the structuring element:\nmaxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))\n\n# Perform closing:\nclosingImage = cv2.morphologyEx(filteredImage, cv2.MORPH_CLOSE, maxKernel, None, None, opIterations, cv2.BORDER_REFLECT101)\n\n# Invert image to obtain black numbers on white background:\nclosingImage = 255 - closingImage\n
Run Code Online (Sandbox Code Playgroud)\n