如何用不同颜色和字体的文本改善图像的OCR?

时间:2018-08-11 20:38:15

标签: python python-imaging-library ocr google-vision

我正在使用Google Vision API从某些图片中提取文本,但是,我一直在努力提高结果的准确性(可信度),但是没有运气。

每次我从原始图像更改图像时,都会丢失检测某些字符的准确性。

我已经解决了这个问题,即不同单词具有多种颜色,例如可以看到红色的单词比其他单词更容易出现错误结果。

示例:

图像的灰度或黑白变化

Original Image

enter image description here

enter image description here

enter image description here

enter image description here

由于大多数算法都希望这样做,我有什么想法可以使它更好地工作,特别是将文本的颜色更改为统一的颜色,或者在白色背景上将颜色更改为黑色?

我已经尝试过的一些想法,也有一些门槛。

dimg = ImageOps.grayscale(im)
cimg = ImageOps.invert(dimg)

contrast = ImageEnhance.Contrast(dimg)
eimg = contrast.enhance(1)

sharp = ImageEnhance.Sharpness(dimg)
eimg = sharp.enhance(1)

6 个答案:

答案 0 :(得分:1)

您几乎尝试了所有标准步骤。我建议您尝试一些PIL内置滤镜,例如清晰度滤镜。在RGB图像上应用锐度和对比度,然后将其二值化。也许使用Image.split()和Image.merge()分别对每种颜色进行二值化处理,然后将它们重新组合在一起。 或将图像转换为YUV,然后仅使用Y通道进行进一步处理。 另外,如果您没有单色背景,请考虑进行一些背景扣除。

当检测到扫描的文本时,tesseract的感觉会被删除,因此您可以尝试破坏图像中尽可能多的非字符空间。 (尽管您可能需要保持图片大小,所以应将其替换为白色)。 Tesseract也喜欢直线。因此,如果以一定角度记录文本,则可能需要进行一些校正。如果将图像调整为原始大小的两倍,Tesseract有时也会提供更好的结果。

我怀疑Google Vision是否使用了tesseract或其中的一部分,但是我不知道它还能为您做哪些其他预处理。因此,我在这里的一些建议实际上可能已经实施,而这样做是不必要和重复的。

答案 1 :(得分:0)

我需要更多上下文信息。

  1. 您打算对Google Vision API进行多少次调用?如果您在整个流程中都这样做,则可能需要获得付费订阅。
  2. 您将如何处理这些数据? OCR需要达到多少精度?
  3. 假设您是从另一个人的抽搐流中获取此快照的,并处理了流媒体的视频压缩和网络连接,那么您将获得非常模糊的快照,因此OCR将会非常困难。

由于视频压缩,图像太模糊,因此,即使对图像进行预处理以提高质量,也可能无法获得足够高的图像质量以进行准确的OCR。如果您设置了OCR,则可以尝试以下一种方法:

  1. 对图像进行二值化处理,以得到与二进制图像一样的白色和黑色背景的非红色文本:

    from PIL import Image
    
    def binarize_image(im, threshold):
    """Binarize an image."""
        image = im.convert('L')  # convert image to monochrome
        bin_im = image.point(lambda p: p > threshold and 255)
        return bin_im
    
    im = Image.open("game_text.JPG")
    binarized = binarize_image(im, 100)
    

enter image description here

  1. 仅使用过滤器提取红色文本值,然后将其二值化:

    import cv2
    from matplotlib import pyplot as plt
    
    lower = [15, 15, 100]
    upper = [50, 60, 200]
    
    lower = np.array(lower, dtype = "uint8")
    upper = np.array(upper, dtype = "uint8")
    
    mask = cv2.inRange(im, lower, upper)
    red_binarized = cv2.bitwise_and(im, im, mask = mask)
    
    plt.imshow(cv2.cvtColor(red_binarized, cv2.COLOR_BGR2RGB))
    plt.show()
    

但是,即使进行了这种过滤,它仍然不能很好地提取红色。

enter image description here

enter image description here

  1. 添加在(1.)和(2.)中获得的图像。

    combined_image = binarized + red_binarized
    

enter image description here

  1. 在(3.)上执行OCR

答案 2 :(得分:0)

您将需要对图像进行多次预处理,并使用bitwise_or操作来组合结果。要提取颜色,可以使用

import cv2
boundaries = [      #BGR colorspace for opencv, *not* RGB
    ([15, 15, 100], [50, 60, 200]),    #red
    ([85, 30, 2], [220, 90, 50]),      #blue
    ([25, 145, 190], [65, 175, 250]),  #yellow
]

for (low, high) in boundaries:
    low = np.array(low, dtype = "uint8")
    high = np.array(high, dtype = "uint8")

    # find the colors within the specified boundaries and apply
    # the mask
    mask = cv2.inRange(image, low, high)
    bitWise = cv2.bitwise_and(image, image, mask=mask)
    #now here is the image masked with the specific color boundary...

一旦有了蒙版图像,就可以对将要成为“最终”图像的图像进行另一次bitwise_or操作,从本质上添加此蒙版。

但是此特定实现需要opencv,但是相同的原理也适用于其他映像包。

答案 3 :(得分:0)

我只能提供屠夫的解决方案,这可能是一场噩梦。

在我自己的非常有限的情况下,它就像一种魅力,其中其他几个OCR引擎出现故障或运行时间不可接受。

我的先决条件:

  • 我确切知道文本要在屏幕的哪个区域。
  • 我确切知道将使用哪种字体和颜色。
  • 文本是半透明的,因此基础图像受到干扰,并且是要引导的可变图像。
  • 我无法可靠地检测到文本平均帧数变化并无法减少干扰。

我做了什么:  -我测量了每个字符的字距调整宽度。我只有A-Za-z0-9和一堆标点符号要担心。  -程序将从位置(0,0)开始,测量平均颜色以确定颜色,然后访问从该颜色的所有可用字体中的字符生成的整个位图集。然后它将确定哪个矩形最靠近屏幕上的相应矩形,然后前进到下一个矩形。

(几个月后,由于需要更高的性能,我添加了一个变化的概率矩阵来首先测试最可能的字符)。

最后,最终的C程序能够以100%的实时性从视频流中读取字幕。

答案 4 :(得分:0)

这不是完整的解决方案,但可能会带来更好的结果。

通过将数据从BGR(或RGB)转换为CIE-La b ,您可以将灰度图像处理为颜色通道a *和b *的加权和。 此灰度图像将增强文本的颜色区域。 但是通过调整阈值,您可以从此灰度图像段中分割原始图像中的彩色单词,并从L通道阈值中获取其他单词。 按位和运算符应足以合并到两个分割图像。

如果您可以得到对比度更好的图像,那么最后一步可能是基于轮廓填充。 为此,请查看函数'cv2.findContours'的RETR_FLOODFILL。 其他包装中的任何其他打孔功能也可能适用于此目的。

这是显示我想法的第一部分的代码。

import cv2
import numpy as np 
from matplotlib import pyplot as plt

I = cv2.UMat(cv2.imread('/home/smile/QSKN.png',cv2.IMREAD_ANYCOLOR))

Lab = cv2.cvtColor(I,cv2.COLOR_BGR2Lab)

L,a,b = cv2.split(Lab)

Ig = cv2.addWeighted(cv2.UMat(a),0.5,cv2.UMat(b),0.5,0,dtype=cv2.CV_32F)

Ig = cv2.normalize(Ig,None,0.,255.,cv2.NORM_MINMAX,cv2.CV_8U)


#k = np.ones((3,3),np.float32)
#k[2,2] = 0
#k*=-1
#
#Ig = cv2.filter2D(Ig,cv2.CV_32F,k)
#Ig = cv2.absdiff(Ig,0)
#Ig = cv2.normalize(Ig,None,0.,255.,cv2.NORM_MINMAX,cv2.CV_8U)



_, Ib = cv2.threshold(Ig,0.,255.,cv2.THRESH_OTSU)
_, Lb = cv2.threshold(cv2.UMat(L),0.,255.,cv2.THRESH_OTSU)

_, ax = plt.subplots(2,2)

ax[0,0].imshow(Ig.get(),cmap='gray')
ax[0,1].imshow(L,cmap='gray')
ax[1,0].imshow(Ib.get(),cmap='gray')
ax[1,1].imshow(Lb.get(),cmap='gray')

答案 5 :(得分:0)

import numpy as np
from skimage.morphology import selem
from skimage.filters import rank, threshold_otsu
from skimage.util import img_as_float
from PIL import ImageGrab
import matplotlib.pyplot as plt

def preprocessing(image, strelem, s0=30, s1=30, p0=.3, p1=1.):
    image = rank.mean_bilateral(image, strelem, s0=s0, s1=s1)
    condition = (lambda x: x>threshold_otsu(x))(rank.maximum(image, strelem))
    normalize_image = rank.autolevel_percentile(image, strelem, p0=p0, p1=p1)
    return np.where(condition, normalize_image, 0)

image = np.array(ImageGrab.grabclipboard())  // grab image from clipboard
sel = selem.disk(4)
a = sum([img_as_float(preprocessing(image[:, :, x], sel, p0=0.3)) for x in range(3)])/3

fig, ax = plt.subplots(1, 2, sharey=True, sharex=True)
ax[0].imshow(image)
ax[1].imshow(rank.autolevel_percentile(a, sel, p0=.4))

这是我的代码,用于清除噪音中的文本并为字符创建均匀的亮度。 经过小的修改,我用它来解决您的问题。

enter image description here