使用YOLO或其他图像识别技术来识别图像中存在的所有字母数字文本。

13

我有多个图像示意图,其中所有标签都包含字母数字字符,而不仅仅是文本标签本身。 我希望我的YOLO模型能够识别其中的所有数字和字母数字字符。

如何训练我的YOLO模型来完成这项任务。 可以在此处找到数据集。 https://drive.google.com/open?id=1iEkGcreFaBIJqUdAADDXJbUrSj99bvoi

例如:请参见边界框。 我希望YOLO能检测出文本所在的位置。 但目前不必识别其中的文本。

enter image description here

还需要针对此类图像执行相同的操作 enter image description here enter image description here

可以在此处下载这些图像here

这是我使用opencv尝试过的方法,但它不能处理数据集中的所有图像。

import cv2
import numpy as np
import pytesseract

pytesseract.pytesseract.tesseract_cmd = r"C:\Users\HPO2KOR\AppData\Local\Tesseract-OCR\tesseract.exe"

image = cv2.imread(r'C:\Users\HPO2KOR\Desktop\Work\venv\Patent\PARTICULATE DETECTOR\PD4.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
clean = thresh.copy()

horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,30))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

cnts = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    if area < 100:
        cv2.drawContours(clean, [c], -1, 0, 3)
    elif area > 1000:
        cv2.drawContours(clean, [c], -1, 0, -1)
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    x,y,w,h = cv2.boundingRect(c)
    if len(approx) == 4:
        cv2.rectangle(clean, (x, y), (x + w, y + h), 0, -1)

open_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
opening = cv2.morphologyEx(clean, cv2.MORPH_OPEN, open_kernel, iterations=2)
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,2))
close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, close_kernel, iterations=4)
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = cv2.contourArea(c)
    if area > 500:
        ROI = image[y:y+h, x:x+w]
        ROI = cv2.GaussianBlur(ROI, (3,3), 0)
        data = pytesseract.image_to_string(ROI, lang='eng',config='--psm 6')
        if data.isalnum():
            cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
            print(data)

cv2.imwrite('image.png', image)
cv2.imwrite('clean.png', clean)
cv2.imwrite('close.png', close)
cv2.imwrite('opening.png', opening)
cv2.waitKey()

有没有任何模型或者opencv技术或一些预训练的模型可以替我完成这个任务?我只需要在图像中找到所有字母数字字符周围的边界框,然后识别出其中的内容。不过目前第二部分并不重要。


那并不适用于所有的图像。 - user11247278
3个回答

8
一种可能的方法是使用基于Zhou等人2017年论文的EAST(高效准确的场景文本)深度学习文本检测器EAST: An Efficient and Accurate Scene Text Detector。该模型最初是为检测自然场景图像中的文本而训练的,但可能可以将其应用于图表图像。EAST非常强大,能够检测模糊或反射的文本。这是Adrian Rosebrock对EAST实现的修改版本。我们可以在执行文本检测之前尝试尽可能多地删除图像上的非文本对象,而不是直接在图像上应用文本检测器。思路是在应用检测之前删除水平线、垂直线和非文本轮廓(曲线、对角线、圆形等)。以下是一些图片的结果:

输入->要删除的非文本轮廓以绿色显示

结果

其他图片

预训练的frozen_east_text_detection.pb模型用于文本检测,可以在这里找到。虽然该模型可以捕获大部分文本,但结果并非100%准确,并且偶尔会出现误报,可能是由于它在自然场景图像上的训练方式造成的。要获得更准确的结果,您可能需要训练自己的定制模型。但如果您想要一个不错的开箱即用解决方案,那么这应该适合您。查看Adrian的OpenCV Text Detection (EAST text detector)博客文章,了解有关EAST文本检测器的更全面的解释。
from imutils.object_detection import non_max_suppression
import numpy as np
import cv2

def EAST_text_detector(original, image, confidence=0.25):
    # Set the new width and height and determine the changed ratio
    (h, W) = image.shape[:2]
    (newW, newH) = (640, 640)
    rW = W / float(newW)
    rH = h / float(newH)

    # Resize the image and grab the new image dimensions
    image = cv2.resize(image, (newW, newH))
    (h, W) = image.shape[:2]

    # Define the two output layer names for the EAST detector model that
    # we are interested -- the first is the output probabilities and the
    # second can be used to derive the bounding box coordinates of text
    layerNames = [
        "feature_fusion/Conv_7/Sigmoid",
        "feature_fusion/concat_3"]

    net = cv2.dnn.readNet('frozen_east_text_detection.pb')

    # Construct a blob from the image and then perform a forward pass of
    # the model to obtain the two output layer sets
    blob = cv2.dnn.blobFromImage(image, 1.0, (W, h), (123.68, 116.78, 103.94), swapRB=True, crop=False)
    net.setInput(blob)
    (scores, geometry) = net.forward(layerNames)

    # Grab the number of rows and columns from the scores volume, then
    # initialize our set of bounding box rectangles and corresponding
    # confidence scores
    (numRows, numCols) = scores.shape[2:4]
    rects = []
    confidences = []

    # Loop over the number of rows
    for y in range(0, numRows):
        # Extract the scores (probabilities), followed by the geometrical
        # data used to derive potential bounding box coordinates that
        # surround text
        scoresData = scores[0, 0, y]
        xData0 = geometry[0, 0, y]
        xData1 = geometry[0, 1, y]
        xData2 = geometry[0, 2, y]
        xData3 = geometry[0, 3, y]
        anglesData = geometry[0, 4, y]

        # Loop over the number of columns
        for x in range(0, numCols):
            # If our score does not have sufficient probability, ignore it
            if scoresData[x] < confidence:
                continue

            # Compute the offset factor as our resulting feature maps will
            # be 4x smaller than the input image
            (offsetX, offsetY) = (x * 4.0, y * 4.0)

            # Extract the rotation angle for the prediction and then
            # compute the sin and cosine
            angle = anglesData[x]
            cos = np.cos(angle)
            sin = np.sin(angle)

            # Use the geometry volume to derive the width and height of
            # the bounding box
            h = xData0[x] + xData2[x]
            w = xData1[x] + xData3[x]

            # Compute both the starting and ending (x, y)-coordinates for
            # the text prediction bounding box
            endX = int(offsetX + (cos * xData1[x]) + (sin * xData2[x]))
            endY = int(offsetY - (sin * xData1[x]) + (cos * xData2[x]))
            startX = int(endX - w)
            startY = int(endY - h)

            # Add the bounding box coordinates and probability score to
            # our respective lists
            rects.append((startX, startY, endX, endY))
            confidences.append(scoresData[x])

    # Apply non-maxima suppression to suppress weak, overlapping bounding
    # boxes
    boxes = non_max_suppression(np.array(rects), probs=confidences)

    # Loop over the bounding boxes
    for (startX, startY, endX, endY) in boxes:
        # Scale the bounding box coordinates based on the respective
        # ratios
        startX = int(startX * rW)
        startY = int(startY * rH)
        endX = int(endX * rW)
        endY = int(endY * rH)

        # Draw the bounding box on the image
        cv2.rectangle(original, (startX, startY), (endX, endY), (36, 255, 12), 2)
    return original

# Convert to grayscale and Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
clean = thresh.copy()

# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,30))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

# Remove non-text contours (curves, diagonals, circlar shapes)
cnts = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    if area > 1500:
        cv2.drawContours(clean, [c], -1, 0, -1)
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    x,y,w,h = cv2.boundingRect(c)
    if len(approx) == 4:
        cv2.rectangle(clean, (x, y), (x + w, y + h), 0, -1)

# Bitwise-and with original image to remove contours
filtered = cv2.bitwise_and(image, image, mask=clean)
filtered[clean==0] = (255,255,255)

# Perform EAST text detection
result = EAST_text_detector(image, filtered)

cv2.imshow('filtered', filtered)
cv2.imshow('result', result)
cv2.waitKey()

直到今天,我仍然感到惊讶的是,在短短的几天内出现了大量人们提出的极其相似的简历问题。这几乎就像来自同一图像处理课程的人正在寻求帮助完成他们的作业,或者只是在寻找有人能替他们完成作业。这真是一个非常奇怪的“巧合”。 - karlphillip
2
@karlphillip,也许这个问题看起来很熟悉,因为OP在大约一周前发布过。他基本上希望有一个可以直接解决所有情况的CTRL+C、CTRL+V答案,所以我猜你可能会在几周后再次看到完全相同的问题! - stateMachine

6
为了方便起见,我想添加这个包keras_ocr。它可以轻松地使用pip进行安装,并基于CRAFT文本检测器开发,如果我没记错的话,它比EAST检测器更新一些。

除了检测之外,它还可以进行一些OCR!结果如下图所示,可以将其视为替代方案,也许比接受的答案更容易实现。enter image description here


嗨,维克多,这对至少70%的我的图片有效吗? - user11247278
你的数据集中没有包含标签。如果我没有一种验证它是否有效的方法,无法告诉你它在多少图像上运行的百分比。但是,它是一个pip软件包,所以你可以很容易地在你的数据集上运行它并自己看看 :) - Victor Sonck

4
您所描述的似乎是OCR(光学字符识别)。我知道的一个OCR引擎是tesseract,还有IBM的这个和其他引擎。
由于YOLO最初是为非常不同的任务而训练的,因此要将其用于定位文本可能需要从头开始重新训练。可以尝试使用现有软件包(适应您特定设置的软件包)来生成真实数据(虽然值得记住,模型通常最多只能与真实数据一样好)。或者更容易的是,生成合成数据进行训练(即在您选择的位置添加文本到现有图纸中,然后训练以定位它)。
另外,如果所有目标图像都结构类似于上面的图像,则可以尝试使用经典CV启发式方法创建真实数据,如上面所做的分离/分割符号,然后使用在MNIST或类似数据集上训练的CNN分类器确定给定的斑点是否包含符号。

如果您选择使用YOLO,有一些现有的Python实现,例如我曾经使用过this one,应该很容易设置自己的ground truth进行训练。

最后,如果使用YOLO或CNN不是目标本身,而只是解决方案,则可以直接使用上述任何“ground truth”作为解决方案,而不是用于训练模型。

希望我正确理解了您的问题。


如果您能提供相应的代码,因为这个问题包含赏金。 - user11247278
任务是最终获取文本,但首先要识别其中的所有字母数字字符,然后使用OCR进行识别。 - user11247278
我提出的任何解决方案都不是一个开箱即用的解决方案,算法代码也不会很短或简单。因此,我只会保持在想法的层面上 :-). 顺便说一句,感谢点赞! - Yuri Feldman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接