文本识别和重构OCR opencv

Posted

技术标签:

【中文标题】文本识别和重构OCR opencv【英文标题】:text recognition and restructuring OCR opencv 【发布时间】:2021-08-18 04:14:18 【问题描述】:

链接到原始图像 https://ibb.co/0VC6vkX

我目前正在处理一个 OCR 项目。我对图像进行了预处理,然后应用预训练的 EAST 模型进行文本检测。

import cv2
import numpy as np
from imutils.object_detection import non_max_suppression
import matplotlib.pyplot as plt
%matplotlib inline

img=cv2.imread('bw_image.jpg')
model=cv2.dnn.readNet('frozen_east_text_detection.pb')

#Prepare the Image
#use multiple of 32 to set the new image shape
height,width,colorch=img.shape
new_height=(height//32)*32
new_width=(width//32)*32
print(new_height,new_width)

h_ratio=height/new_height
w_ratio=width/new_width
print(h_ratio,w_ratio)

#blob from image helps us to prepare the image
blob=cv2.dnn.blobFromImage(img,1,(new_width,new_height),(123.68,116.78,103.94),True, False)
model.setInput(blob)

#this model outputs geometry and score maps
(geometry,scores)=model.forward(model.getUnconnectedOutLayersNames())

#once we have done geometry and score maps we have to do post processing to obtain the final text boxes
rectangles=[]
confidence_score=[]
for i in range(geometry.shape[2]):
    for j in range(0,geometry.shape[3]):
    
        if scores[0][0][i][j]<0.1:
            continue

        bottom_x=int(j*4 + geometry[0][1][i][j])
        bottom_y=int(i*4 + geometry[0][2][i][j])

        top_x=int(j*4 - geometry[0][3][i][j])
        top_y=int(i*4 - geometry[0][0][i][j])

        rectangles.append((top_x,top_y,bottom_x,bottom_y))
        confidence_score.append(float(scores[0][0][i][j]))

#use nms to get required triangles
final_boxes=non_max_suppression(np.array(rectangles),probs=confidence_score,overlapThresh=0.5)

#finally to display these text boxes let's iterate over them and convert them to the original shape 
#using the ratio we calculated earlier
img_copy=img.copy()

for (x1,y1,x2,y2) in final_boxes:
    
    x1=int(x1*w_ratio)
    y1=int(y1*h_ratio)
    x2=int(x2*w_ratio)
    y2=int(y2*h_ratio)
    
    #to draw the rectangles on the image use cv2.rectangle function
    cv2.rectangle(img_copy,(x1,y1),(x2,y2),(0,255,0),2)

这为我们提供了检测到的文本,如下所示:

现在对于文本识别,我使用了预训练的 opencv CRNN 模型,如下所示:

# Download the CRNN model and Load it
model1 = cv2.dnn.readNet('D:/downloads/crnn.onnx')


# ## Prepare the image
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

blob = cv2.dnn.blobFromImage(img_gray, scalefactor=1/127.5, size=(100,32), mean=127.5)


# Pass the image to network and extract per-timestep scores
model1.setInput(blob)

scores = model1.forward()
print(scores.shape)

alphabet_set = "0123456789abcdefghijklmnopqrstuvwxyz"
blank = '-'

char_set = blank + alphabet_set


# Decode the scores to text
def most_likely(scores, char_set):
    text = ""
    for i in range(scores.shape[0]):
        c = np.argmax(scores[i][0])
        text += char_set[c]
    return text


def map_rule(text):
    char_list = []
    for i in range(len(text)):
        if i == 0:
            if text[i] != '-':
                char_list.append(text[i])
        else:
            if text[i] != '-' and (not (text[i] == text[i - 1])):
                char_list.append(text[i])
    return ''.join(char_list)


def best_path(scores, char_set):
    text = most_likely(scores, char_set)
    final_text = map_rule(text)
    return final_text


out = best_path(scores, char_set)
print(out)

但是在图像上应用这个模型会得到以下输出:

saetan

我真的不明白。任何人都可以指导文本识别有什么问题。预训练的 CRNN 模型有问题吗?此外,我还想在文本被识别后对其进行重组,以它们在原始图像中的结构方式。识别问题解决后,我们有了边界框坐标和识别文本,那么我们如何准确地重构文本呢?任何帮助将不胜感激。

编辑:我使用了 pytesseract image_to_string()image_to_data() 函数,但它们的性能并不好。是否有任何其他我可以使用的预训练文本识别模型,以便如果这个 CRNN 模型不够合适,我可以复制我的 EAST Text Detection 模型的成功。这样我就可以在通过 EAST 模型获得的coordinates(bounding boxes) 的帮助下准确地重构图像中的文本。

【问题讨论】:

是否必须使用 EAST 模型 (crnn.onnx) 进行文本检测?如果没有,我建议使用pytesseract。特别是 image_to_string()image_to_data() 函数。 我已经尝试过pytesseract 和您提到的功能,但它们并没有给出准确的结果。也许这是由于 pytesseract 不能很好地处理低质量图像的原因。我更喜欢 EAST 模型和 CRNN 预训练模型,因为如图所示,EAST 模型非常准确地检测到文本,我也想在文本识别中复制这种成功。 您是在整个图像上还是在单个边界框上应用pytesseract?我通常只在检索边界框后才使用pytesseract,但性能通常非常好。您的图像质量不够差,pytesseract 无法通过 IMO。也许尝试一些more preprocessing。您的边界框也确实受到限制,字母需要完全在框内才能使 OCR 工作。也许尝试添加一些填充像素。 您需要提供CRNN(或pytesseract)检测到的边界框(我怀疑是一次一个)而不是整个图像。您需要遍历所有框。 我认为改写我的问题的更好方法是如何遍历通过 East Model 检测到的边界框并按照@Shai 的建议识别这些边界框中的文本 【参考方案1】:

处理作物真的很简单,只需稍微改变一下你的最后一个循环:

import pytesseract
from PIL import Image

...
 
for x1,y1,x2,y2 in final_boxes:
        
    #to draw the rectangles on the image use cv2.rectangle function
    # cv2.rectangle(img_copy,(x1,y1),(x2,y2),(0,255,0),2)
    img_crop = Image.fromarray(img[y1-1: y2+1, x1-1:x2+1])
    text = pytesseract.image_to_string(img_crop, config='--psm 8').strip()
    cv2.putText(img_copy, text, (x1,y1), 0, .7, (0, 0, 255), 2 )

【讨论】:

这确实解决了我的问题,而无需使用预训练的 crnn 模型。您能否指导我在一些 word 或 pdf 文件上重新构造这个识别的文本,就像上面收据中的结构一样。 你可以看到我的另一个答案,正好处理preserving indentation的问题。垂直和从左到右对框进行排序,然后应用代码。 @Junaid,您接受的答案是我写的作为对我的第一个解决方案的评论。在此之后您清楚地写道,您需要 CRNN 的解决方案,这就是我提供给您的第二个答案。【参考方案2】:

这是一个可能的解决方案,您可以通过尝试一些方法来改进:

通过改变高斯参数 通过对模糊图像进行阈值处理来查看它是否可以改善结果

代码:

gray = cv2.imread('/path/to/your_image.jpeg', cv2.IMREAD_GRAYSCALE)

g = cv2.GaussianBlur(gray, (3, 3), .5)

config = "-l eng --oem 1 --psm 6"
text = pytesseract.image_to_string(g, config=config)
print(text)

结果文本(部分):

400242 | 6161108006012 BIO WHOLE MILK 1LTR \ 1PCS 12.Cu PCS 430.50 1,566.00
400365 | 6161108000119 BIO YOG VANILLA 150ML CUP ! 1PCS 24.05 PCS 91.02 2,184.36
400545 | 6161108000584 BIO LONG LIFE COOKING CREAM SOOML 1 1PCS \ 12.Gu PCS 241.32 2,895.78
74 - i :
400821 | 6161108005060" | BIO YOGHURT STRAWBERRY 450ML | 1Pcs 6.50 PCS 266.37 1,598.23
400822 , 6161108005207 BIO YOGHURT VANILLA 90ML ; 1PCS ! 36.0b FCS 60.96 2,194.38
450466 | 6166000051801 KENTASTE COCONUT MILK 400ML ; 1CTN * 12 PCS | 2.00 TN 1,920.96 3,841.92
, 450469 | 6166000051818 KENTASTE COCONUT CREAM 400ML : 1CTN* 12 PCS | 2.0. CTN 2,213.28 4,426.56
450465 | 6166000051887 KENTASTE COCONUT OIL 700ML 1CTN * 12 PCS | Iso) STN) 7,697.76 7,697.76
400985 | 6161108000812 BIO WHOLE MILK LONG LIFE SOOML EPCS: 12.00 PCS | 67.40 808.79

【讨论】:

我使用了 pytesseract,与您的相比,它在不使用高斯模糊的情况下返回了相当不错的结果。我认为重新表述我的问题的更好方法是如何遍历通过 East Model 检测到的边界框并识别这些边界框中的文本。 你不能在你的 for 循环中对边界框图像运行 pytesserect 吗?这种方法有什么问题?在我的测试中,稍微模糊图像给了我更好的结果。 无论如何,稍微预处理你的测试图像肯定会改善结果。 @Junaid,您接受的答案是我在这里写的作为解决方案的内容(请参阅我的第二条评论)。在这之后你清楚地写道,你需要一个 CRNN 的解决方案,这就是我提供给你的。【参考方案3】:

经过仔细检查,我发现您的代码有很多问题。如果我了解您希望如何运行 crnn 识别器,那么以下解决方案可能会解决您的问题。

你的函数定义:

# Decode the scores to text
def most_likely(scores, char_set):
    text = ""
    for i in range(scores.shape[0]):
        c = np.argmax(scores[i][0])
        text += char_set[c]
    return text


def map_rule(text):
    char_list = []
    for i in range(len(text)):
        if i == 0:
            if text[i] != '-':
                char_list.append(text[i])
        else:
            if text[i] != '-' and (not (text[i] == text[i - 1])):
                char_list.append(text[i])
    return ''.join(char_list)


def best_path(scores, char_set):
    text = most_likely(scores, char_set)
    final_text = map_rule(text)
    return final_text

以下大部分代码是您已有的,比较变化:

import cv2
import numpy as np
from imutils.object_detection import non_max_suppression
import matplotlib.pyplot as plt
%matplotlib inline

img=cv2.imread('/path/to/your_imge.jpeg')
model=cv2.dnn.readNet('/path/to/frozen_east_text_detection.pb')

#Prepare the Image
#use multiple of 32 to set the new image shape
height,width,colorch=img.shape
new_height=(height//32)*32
new_width=(width//32)*32
print(new_height,new_width)

h_ratio=height/new_height
w_ratio=width/new_width
print(h_ratio,w_ratio)

#blob from image helps us to prepare the image
blob=cv2.dnn.blobFromImage(img,1,(new_width,new_height),(123.68,116.78,103.94),True, False)
model.setInput(blob)

#this model outputs geometry and score maps
(geometry,scores)=model.forward(model.getUnconnectedOutLayersNames())

#once we have done geometry and score maps we have to do post processing to obtain the final text boxes
rectangles=[]
confidence_score=[]
for i in range(geometry.shape[2]):
    for j in range(0,geometry.shape[3]):
    
        if scores[0][0][i][j]<0.1:
            continue

        bottom_x=int(j*4 + geometry[0][1][i][j])
        bottom_y=int(i*4 + geometry[0][2][i][j])

        top_x=int(j*4 - geometry[0][3][i][j])
        top_y=int(i*4 - geometry[0][0][i][j])

        rectangles.append((top_x,top_y,bottom_x,bottom_y))
        confidence_score.append(float(scores[0][0][i][j]))

#use nms to get required triangles
final_boxes=non_max_suppression(np.array(rectangles),probs=confidence_score,overlapThresh=0.5)

model1 = cv2.dnn.readNet('/path/to/crnn.onnx')


# ## Prepare the image
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

alphabet_set = "0123456789abcdefghijklmnopqrstuvwxyz"
blank = '-'

char_set = blank + alphabet_set

for (x1,y1,x2,y2) in final_boxes:
    
    x1=int(x1*w_ratio)
    y1=int(y1*h_ratio)
    x2=int(x2*w_ratio)
    y2=int(y2*h_ratio)
    
    # Work with detected text boxes for recognition
    blob = cv2.dnn.blobFromImage(img_gray[y1:y2, x1:x2], scalefactor=1/127.5, size=(100,32), mean=127.5)

    # Pass the image to network and extract per-timestep scores
    model1.setInput(blob)

    scores = model1.forward()
    print(scores.shape)

    out = best_path(scores, char_set)
    print(out)

您可能必须将已识别的文本覆盖在图像顶部左右以检查准确性。有更好的方法来进行测试,但这超出了这个问题的范围。

【讨论】:

@Junaid,代码是否按您想要的方式工作?我确信准确性可能是一个问题,但它至少应该遍历所有检测到的文本框并打印识别的字符。 是的,它确实有效,但会逐个显示已识别的字符,这不是错误的方法,但我一直在寻找更强大的方法。是的,您在其他答案的评论中给出了答案,但我不知道该怎么做。无论如何,谢谢。

以上是关于文本识别和重构OCR opencv的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 OpenCV 进行 OCR 和文本检测识别

Python+OpenCV+Tesseract实现OCR字符识别

OCR场景文本识别:文字检测+文字识别

如何识别作为光学字符识别 (OCR) 输出的文本中的实体?

OCR文字识别软件拆分对页和校正文本方向的教程

用于屏幕文本的 OCR(光学字符识别)