以下是一种可能的解决方案。这是Python代码,但是对于Java移植应该足够清晰。我们将应用一种称为“增益分割”的方法。思路是尝试构建背景模型,然后通过该模型对每个输入像素进行加权。输出增益应在大部分图像中保持相对恒定。这将消除大部分背景颜色变化。我们可以使用形态学链稍微清理结果,下面是代码:
import cv2
import numpy as np
from PIL import Image
import pyocr
import pyocr.builders
path = "D://opencvImages//"
fileName = "c552h.png"
inputImage = cv2.imread(path + fileName)
kernelSize = 5
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
localMax = cv2.morphologyEx(inputImage, cv2.MORPH_CLOSE, maxKernel, None, None, 1, cv2.BORDER_REFLECT101)
gainDivision = np.where(localMax == 0, 0, (inputImage/localMax))
gainDivision = np.clip((255 * gainDivision), 0, 255)
gainDivision = gainDivision.astype("uint8")
第一步是应用增益分割,您需要的操作很简单:使用一个大矩形结构元素进行形态学闭运算和一些数据类型转换,要小心后者。此方法应用后应看到以下图像:
非常酷,背景几乎消失了。现在让我们使用Otsu阈值法得到二值图像:
grayscaleImage = cv2.cvtColor(gainDivision, cv2.COLOR_BGR2GRAY)
_, binaryImage = cv2.threshold(grayscaleImage, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
这是二值图像:
![](https://i.imgur.com/nNzCTo0.png)
我们有一张很好的文本边缘图像。如果我们用白色填充背景,我们可以得到黑色背景和白色文本。但是,我们必须注意字符,因为如果一个字符被打断了,
Flood-Fill
操作会将其擦除。让我们先通过应用形态学的
closing
确保我们的字符是闭合的:
kernelSize = 3
opIterations = 1
morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
binaryImage = cv2.morphologyEx( binaryImage, cv2.MORPH_CLOSE, morphKernel, None, None, opIterations, cv2.BORDER_REFLECT101 )
这是生成的图像:
如您所见,边缘更加明显,最重要的是它们是封闭的。现在,我们可以用白色填充背景。这里,Flood-Fill
种子点位于图像原点(x = 0
,y = 0
):
cv2.floodFill(binaryImage, mask=None, seedPoint=(int(0), int(0)), newVal=(255))
我们得到这张图片:
![](https://i.imgur.com/KexJ7L8.png)
我们已经接近成功了。您可以看到,一些字符内部的孔(例如"a"、"d"和"o")没有填充 - 这可能会产生干扰
OCR
。让我们来试着填充它们。我们可以利用这些孔都是父轮廓的子轮廓这个事实。我们可以隔离子轮廓,并再次应用
Flood-Fill
来填充它们。但首先,不要忘记反转图像:
binaryImage = 255 - binaryImage
contours, hierarchy = cv2.findContours(binaryImage, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for i, c in enumerate(contours):
currentHierarchy = hierarchy[0][i][3]
if currentHierarchy != -1:
boundRect = cv2.boundingRect(c)
rectX = boundRect[0]
rectY = boundRect[1]
rectWidth = boundRect[2]
rectHeight = boundRect[3]
fx = rectX + 0.5 * rectWidth
fy = rectY + 0.5 * rectHeight
cv2.floodFill(binaryImage, mask=None, seedPoint=(int(fx), int(fy)), newVal=(0))
cv2.imwrite("text.png", binaryImage, [cv2.IMWRITE_PNG_COMPRESSION, 0])
这是生成的掩码:
好的,让我们应用 OCR
。我使用的是 pyocr
:
txt = tool.image_to_string(
Image.open("text.png"),
lang=lang,
builder=pyocr.builders.TextBuilder()
)
print(txt)
输出:
Landorus