Usando o YOLO ou outras técnicas de reconhecimento de imagem para identificar todo o texto alfanumérico presente nas imagens

12

Eu tenho um diagrama de várias imagens, que contém rótulos como caracteres alfanuméricos, em vez de apenas o rótulo do texto. Quero que meu modelo YOLO identifique todos os números e caracteres alfanuméricos presentes nele.

Como posso treinar meu modelo YOLO para fazer o mesmo. O conjunto de dados pode ser encontrado aqui. https://drive.google.com/open?id=1iEkGcreFaBIJqUdAADDXJbUrSj99bvoi

Por exemplo: veja as caixas delimitadoras. Quero que o YOLO detecte onde quer que o texto esteja presente. No entanto, atualmente não é necessário identificar o texto dentro dele.

insira a descrição da imagem aqui

Também é necessário fazer o mesmo para esse tipo de imagem insira a descrição da imagem aqui insira a descrição da imagem aqui

As imagens podem ser baixadas aqui

É isso que tentei usar o opencv, mas ele não funciona para todas as imagens no conjunto de dados.

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()

Existe algum modelo, técnica aberta ou algum modelo pré-treinado que possa fazer o mesmo por mim? Eu só preciso das caixas delimitadoras em torno de todos os caracteres alfanuméricos presentes nas imagens. Depois disso, preciso identificar o que está presente nele. No entanto, a segunda parte não é importante atualmente.

Pulkit Bhatnagar
fonte
Isso responde sua pergunta? OpenCV! _Src.empty () na função 'cvtColor' erro
Amit Yadav
que não funciona para todas as imagens
Pulkit Bhatnagar

Respostas:

7

Uma abordagem possível é usar o detector de texto de aprendizado profundo EAST (Texto de cena eficiente e preciso) com base no artigo de Zhou et al. : EAST: Um detector de texto de cena eficiente e preciso . O modelo foi originalmente treinado para detectar texto em imagens de cenas naturais, mas pode ser possível aplicá-lo em imagens de diagrama. O EAST é bastante robusto e é capaz de detectar texto borrado ou reflexivo. Aqui está uma versão modificada da implementação do EAST de Adrian Rosebrock. Em vez de aplicar o detector de texto diretamente na imagem, podemos tentar remover o máximo de objetos que não sejam de texto na imagem antes de executar a detecção de texto. A idéia é remover linhas horizontais, verticais e contornos que não sejam de texto (curvas, diagonais, formas circulares) antes de aplicar a detecção. Aqui estão os resultados com algumas de suas imagens:

Entrada ->de contornos não textuais para remover em verde

Resultado

Outras imagens

O frozen_east_text_detection.pbmodelo pré-treinado necessário para executar a detecção de texto pode ser encontrado aqui . Embora o modelo capte a maior parte do texto, os resultados não são 100% precisos e apresentam falsos positivos ocasionais, provavelmente devido à forma como foi treinado em imagens de cenas naturais. Para obter resultados mais precisos, você provavelmente precisará treinar seu próprio modelo personalizado. Mas se você deseja uma solução pronta e decente, isso deve funcionar com você. Confira a detecção de texto OpenCV de Adrian (detector de texto EAST) publicação do blog de para obter uma explicação mais abrangente do detector de texto EAST.

Código

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
fonte
Resposta muito completa. Quantas horas de esforço?
karlphillip 26/02
Cerca de uma hora e mais 30 minutos para escrevê-lo
nathancy 26/02
Até hoje, ainda estou surpreso com o número de pessoas que aparecem com perguntas de currículo extremamente semelhantes em questão de dias. Quase parece que pessoas da mesma classe de processamento de imagens estão procurando ajuda para terminar a lição de casa ou apenas procurando alguém para fazer a lição de casa por eles. É uma "coincidência" realmente bizarra.
karlphillip 26/02
2
@karlphillip Talvez esta pergunta pareça familiar porque o OP a publicou cerca de uma semana atrás. Ele quer uma resposta CTRL + C, CTRL + V que cubra todos os seus casos imediatamente, então, acho que você poderá ver essa mesma pergunta novamente em algumas semanas!
eldesgraciado 27/02
3
@eldesgraciado Acabei de perceber que o OP postou uma pergunta semelhante há algumas semanas. Nem sabia que era a mesma pessoa até agora! Eu também queria saber por que a pergunta parecia muito familiar
nathancy
6

Por conveniência, eu gostaria de adicionar o pacote keras_ocr . Ele pode ser instalado facilmente com o pip e é baseado no detector de texto CRAFT, que é um pouco mais novo que o detector EAST, se não estiver errado.

Ao lado da detecção, ele também já faz OCR! Os resultados são os seguintes: veja isso como uma alternativa, talvez mais fácil de implementar, do que a resposta aceita.insira a descrição da imagem aqui

Victor Sonck
fonte
Oi victor, ele funciona para pelo menos 70% das minhas imagens?
Pulkit Bhatnagar
Você não incluiu rótulos no seu conjunto de dados. Então, eu realmente não posso te dizer em que% das imagens elas funcionam, se eu não tiver uma maneira de verificar se funcionou comparando-a a um rótulo. No entanto, é um pacote de semente, por isso deve ser fácil o suficiente para que você possa executá-lo em seu conjunto de dados e veja por si mesmo :)
Victor Sonck
4

O que você está descrevendo parece ser OCR ( reconhecimento óptico de caracteres ). Um mecanismo de OCR que conheço é o tesseract , embora exista também este da IBM e de outros.

Como o YOLO foi originalmente treinado para uma tarefa muito diferente, para usá-lo para localizar texto, provavelmente será necessário treiná-lo do zero. Pode-se tentar usar pacotes existentes (adaptados à sua configuração específica) para a verdade básica (embora valha a pena lembrar que o modelo geralmente seria tão bom quanto a verdade básica). Ou, talvez mais facilmente, gere dados sintéticos para treinamento (por exemplo, adicione texto nas posições que você escolher nos desenhos existentes e treine para localizá-los).

Como alternativa, se todas as suas imagens-alvo estiverem estruturadas de maneira semelhante à anterior, tente criar uma verdade básica usando heurísticas clássicas de CV, como você fez acima, para separar / segmentar símbolos, seguido de classificação usando uma CNN treinada em MNIST ou similar para determinar se um determinado blob contiver um símbolo.

No caso de você optar pelo YOLO - existem implementações existentes em python, por exemplo, eu tive alguma experiência com este - deve ser bastante simples configurar o treinamento com sua própria verdade básica.

Finalmente, se o uso do YOLO ou da CNN não é um objetivo em si, mas apenas a solução, qualquer uma das "verdades fundamentais" acima pode ser usada diretamente como solução, e não para o treinamento de um modelo.

Espero ter entendido sua pergunta corretamente

Yuri Feldman
fonte
Se você pode dar o código para o mesmo, como esta pergunta contém recompensa
Pulkit Bhatnagar
a tarefa é finalmente obter o texto, mas primeiro estou tentando identificar todos os caracteres alfanuméricos nele e depois usar o OCR para o mesmo que foi identificado
Pulkit Bhatnagar
Nada do que eu propus é realmente uma solução pronta para uso, e acho que o código algorítmico não seria curto ou simples, então deixarei no nível das idéias :-). ps obrigado pelo voto positivo!
Yuri Feldman