Использование YOLO или других методов распознавания изображений для идентификации всего буквенно-цифрового текста, присутствующего в изображениях

12

У меня есть схема с несколькими изображениями, каждая из которых содержит метки в виде буквенно-цифровых символов, а не только текстовую метку. Я хочу, чтобы моя модель YOLO идентифицировала все цифры и буквенно-цифровые символы, присутствующие в ней.

Как я могу обучить свою модель YOLO делать то же самое. Набор данных можно найти здесь. https://drive.google.com/open?id=1iEkGcreFaBIJqUdAADDXJbUrSj99bvoi

Например: см. Ограничивающие рамки. Я хочу, чтобы YOLO обнаружил, где присутствует текст. Однако в настоящее время нет необходимости идентифицировать текст внутри него.

введите описание изображения здесь

То же самое должно быть сделано для этих типов изображений введите описание изображения здесь введите описание изображения здесь

Изображения можно скачать здесь

Это то, что я пытался использовать 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 или какая-то предварительно обученная модель, которая может сделать то же самое для меня? Мне просто нужны ограничивающие рамки вокруг всех буквенно-цифровых символов, присутствующих на изображениях. После этого мне нужно определить, что в нем присутствует. Однако вторая часть в настоящее время не важна.

Пулькит Бхатнагар
источник
Отвечает ли это на ваш вопрос? OpenCV! _Src.empty () в функции 'cvtColor' ошибка
Амит Ядав
это не работает для всех изображений
Pulkit Bhatnagar

Ответы:

7

Возможный подход заключается в использовании детектора текста EAST (эффективный и точный текст сцены) на основе статьи Чжоу и др. 2017 года, EAST: эффективный и точный детектор текста сцены . Изначально модель была подготовлена ​​для обнаружения текста в естественных изображениях сцены, но может быть возможно применить его к изображениям диаграммы. EAST достаточно устойчив и способен обнаруживать размытый или отражающий текст. Вот модифицированная версия реализации EAST Адриана Роузброка, Вместо того, чтобы применять детектор текста непосредственно к изображению, мы можем попытаться удалить как можно больше нетекстовых объектов на изображении перед выполнением обнаружения текста. Идея состоит в том, чтобы удалить горизонтальные линии, вертикальные линии и нетекстовые контуры (кривые, диагонали, круглые формы) перед применением обнаружения. Вот результаты с некоторыми вашими изображениями:

Ввод ->нетекстовых контуров для удаления зеленым цветом

Результат

Другие изображения

Предварительно обученную frozen_east_text_detection.pbмодель, необходимую для обнаружения текста, можно найти здесь . Хотя модель улавливает большую часть текста, результаты не являются точными на 100% и иногда дают ложные срабатывания, вероятно, из-за того, как она обучалась на естественных изображениях сцены. Чтобы получить более точные результаты, вам, вероятно, придется тренировать собственную модель. Но если вы хотите достойное готовое решение, то это должно сработать. Посмотрите сообщение в блоге Adrian's OpenCV Text Detection (EAST text детектор) для более подробного объяснения детектора текста 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()
nathancy
источник
Очень полный ответ. Сколько часов усилий?
Карлфиллип
Около часа и еще 30 минут, чтобы написать это
Натанси
По сей день я все еще удивляюсь количеству людей, которые появляются с чрезвычайно похожими вопросами резюме в течение нескольких дней. Похоже, что парни из того же класса обработки изображений ищут помощи, чтобы закончить свою домашнюю работу или просто ищут кого-то, кто сделает домашнюю работу для них. Это действительно странное «совпадение».
Карлфиллип
2
@karlphillip Может быть, этот вопрос выглядит знакомым, потому что ОП опубликовал его около недели назад. Он очень хочет получить ответ CTRL + C, CTRL + V, который охватывает все его дела прямо из коробки, так что, я думаю, вы можете увидеть этот же вопрос снова через пару недель!
eldesgraciado
3
@eldesgraciado Я только что понял, что ОП опубликовал похожий вопрос несколько недель назад. До сих пор даже не осознавал, что это был один и тот же человек! Мне также было интересно, почему вопрос выглядел очень знакомым
Натанси
6

Для удобства я хотел бы добавить пакет keras_ocr . Он может быть легко установлен с помощью pip и основан на текстовом детекторе CRAFT, который немного новее, чем детектор EAST, если я не ошибаюсь.

Рядом с обнаружением он уже делает некоторые OCR тоже! Результаты приведены ниже. Рассматривайте это как альтернативу, возможно, проще для реализации, чем принятый ответ.введите описание изображения здесь

Виктор Сонк
источник
Привет Виктор, это работает по крайней мере для 70% моих изображений?
Пулькит Бхатнагар
Вы не включили метки в свой набор данных. Поэтому я не могу точно сказать, на сколько% изображений он работает, если у меня нет способа проверить, сработал ли он, сравнив его с меткой. Тем не менее, это пакет в пипсах, поэтому вам должно быть достаточно легко запустить его в своем наборе данных и убедиться в этом :)
Виктор Сонк
4

То, что вы описываете, кажется, OCR ( Оптическое распознавание символов ). Я знаю один механизм распознавания текста - это tesseract , хотя есть еще один от IBM и других.

Поскольку YOLO изначально был подготовлен для совершенно другой задачи, использование его для локализации текста, вероятно, потребует переобучить его с нуля. Можно попытаться использовать существующие пакеты (адаптированные к вашим конкретным настройкам) для обоснованной правды (хотя стоит помнить, что модель, как правило, будет в лучшем случае такой же хорошей, как и основополагающая правда). Или, возможно, более просто генерировать синтетические данные для обучения (т. Е. Добавить текст в выбранных вами позициях к существующим чертежам, а затем обучиться их локализации).

В качестве альтернативы, если все ваши целевые изображения структурированы аналогично приведенному выше, можно попытаться создать основную правду, используя классическую CV эвристику, как вы делали выше для разделения / сегментирования символов, с последующей классификацией с использованием CNN, обученного по MNIST или подобному, для определения если данный BLOB-объект содержит символ.

Для случая, когда вы выбираете YOLO - в Python есть существующие реализации, например, у меня был некоторый опыт с этим - должно быть довольно просто настроить обучение с вашей собственной основной правдой.

Наконец, если использование YOLO или CNN не является самоцелью, а скорее только решением, любая из вышеупомянутых «основных истин» может быть использована непосредственно в качестве решения, а не для обучения модели.

Надеюсь, я правильно понял ваш вопрос

Юрий Фельдман
источник
Если вы можете дать код для того же, так как этот вопрос содержит щедрость
Pulkit Bhatnagar
задача состоит в том, чтобы в конечном итоге получить текст, но я сначала пытаюсь идентифицировать все буквенно-цифровые символы в нем, а затем использую OCR для того же идентифицированного
текста
Ничто из того, что я предложил, на самом деле не является готовым решением, и я думаю, что алгоритмический код не будет коротким или простым, поэтому я оставлю его на уровне идей :-). ps спасибо за отзыв!
Юрий Фельдман