Usar YOLO u otras técnicas de reconocimiento de imágenes para identificar todo el texto alfanumérico presente en las imágenes.


12

Tengo un diagrama de múltiples imágenes, todas las cuales contienen etiquetas como caracteres alfanuméricos en lugar de solo la etiqueta de texto en sí. Quiero que mi modelo YOLO identifique todos los números y caracteres alfanuméricos presentes en él.

¿Cómo puedo entrenar a mi modelo YOLO para que haga lo mismo? El conjunto de datos se puede encontrar aquí. https://drive.google.com/open?id=1iEkGcreFaBIJqUdAADDXJbUrSj99bvoi

Por ejemplo: ver los cuadros delimitadores. Quiero que YOLO detecte donde esté presente el texto. Sin embargo, actualmente no es necesario identificar el texto dentro de él.

ingrese la descripción de la imagen aquí

También es necesario hacer lo mismo para este tipo de imágenes. ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Las imágenes se pueden descargar aquí.

Esto es lo que he intentado usar opencv pero no funciona para todas las imágenes en el conjunto de datos.

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

¿Hay algún modelo o alguna técnica de opencv o algún modelo previamente entrenado que pueda hacer lo mismo por mí? Solo necesito los cuadros delimitadores alrededor de todos los caracteres alfanuméricos presentes en las imágenes. Después de eso, necesito identificar qué hay en él. Sin embargo, la segunda parte no es importante actualmente.




eso no funciona para todas las imágenes
Pulkit Bhatnagar

Respuestas:


7

Un posible enfoque es utilizar el detector de texto de aprendizaje profundo EAST (Texto de escena eficiente y preciso) basado en el artículo de Zhou et al. De 2017, EAST: un detector de texto de escena eficiente y preciso . El modelo fue originalmente entrenado para detectar texto en imágenes de escenas naturales, pero es posible aplicarlo en imágenes de diagrama. EAST es bastante robusto y es capaz de detectar texto borroso o reflexivo. Aquí hay una versión modificada de la implementación de EAST de Adrian Rosebrock. En lugar de aplicar el detector de texto directamente en la imagen, podemos intentar eliminar la mayor cantidad de objetos que no sean de texto en la imagen antes de realizar la detección de texto. La idea es eliminar líneas horizontales, líneas verticales y contornos que no sean de texto (curvas, diagonales, formas circulares) antes de aplicar la detección. Aquí están los resultados con algunas de sus imágenes:

Ingrese ->contornos sin texto para eliminar en verde

Resultado

Otras imagenes

El frozen_east_text_detection.pbmodelo previamente entrenado necesario para realizar la detección de texto se puede encontrar aquí . Aunque el modelo capta la mayor parte del texto, los resultados no son 100% precisos y ocasionalmente tienen falsos positivos, probablemente debido a cómo fue entrenado en imágenes de escenas naturales. Para obtener resultados más precisos, probablemente tenga que entrenar su propio modelo personalizado. Pero si desea una solución decente lista para usar, esto debería funcionar. Consulte la publicación de blog de Adrian OpenCV Text Detection (detector de texto EAST) para obtener una explicación más completa del 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()

Muy completa respuesta. Cuantas horas de esfuerzo?
karlphillip

Aproximadamente una hora y otros 30 minutos para escribirlo
nathancy

A día de hoy todavía me sorprende la cantidad de personas que aparecen con preguntas de CV extremadamente similares en cuestión de días. Casi parece que los chicos de la misma clase de procesamiento de imágenes buscan ayuda para terminar su tarea o simplemente buscan a alguien que haga la tarea por ellos. Es una "coincidencia" realmente extraña.
karlphillip

2
@karlphillip Quizás esta pregunta parezca familiar porque el OP la publicó hace una semana. Él prácticamente quiere una respuesta CTRL + C, CTRL + V que cubra todos sus casos desde el primer momento, así que, ¡creo que puede ver esta misma pregunta nuevamente en un par de semanas!
eldesgraciado

3
@eldesgraciado Me acabo de dar cuenta de que OP publicó una pregunta similar hace unas semanas. ¡Ni siquiera me di cuenta de que era la misma persona hasta ahora! También me preguntaba por qué la pregunta parecía muy familiar
nathancy

6

Por conveniencia, me gustaría agregar el paquete keras_ocr . Se puede instalar fácilmente con pip y se basa en el detector de texto CRAFT, que es un poco más nuevo que el detector EAST si no me equivoco.

Al lado de la detección, ¡ya hace algo de OCR también! Los resultados se muestran a continuación, vea esto como una alternativa, quizás más fácil de implementar, que la respuesta aceptada.ingrese la descripción de la imagen aquí


Hola victor, ¿funciona para al menos el 70% de mis imágenes?
Pulkit Bhatnagar

No ha incluido etiquetas en su conjunto de datos. Entonces, realmente no puedo decirte en qué porcentaje de las imágenes funciona, si no tengo una manera de verificar si funcionó comparándolo con una etiqueta. Sin embargo, es un paquete pip, por lo que debería ser lo suficientemente fácil para que lo ejecutes en tu conjunto de datos y lo veas por ti mismo :)
Victor Sonck

4

Lo que estás describiendo parece ser OCR ( reconocimiento óptico de caracteres ). Un motor de OCR que conozco es tesseract , aunque también existe este de IBM y otros.

Como YOLO fue entrenado originalmente para una tarea muy diferente, usarlo para localizar texto probablemente requerirá volver a entrenarlo desde cero. Se podría tratar de usar paquetes existentes (adaptados a su configuración específica) para la verdad básica (aunque vale la pena recordar que el modelo generalmente solo sería tan bueno como la verdad básica). O, quizás más fácilmente, genere datos sintéticos para el entrenamiento (es decir, agregue texto en las posiciones que elija a los dibujos existentes y luego entrene para localizarlo).

Alternativamente, si todas sus imágenes de destino están estructuradas de manera similar a la anterior, se podría tratar de crear una verdad básica utilizando la heurística CV clásica como lo hizo anteriormente para separar / segmentar símbolos, seguido de la clasificación utilizando un CNN entrenado en MNIST o similar para determinar si un blob dado contiene un símbolo.

Para el caso de que opte por YOLO, hay implementaciones existentes en python, por ejemplo, tenía algo de experiencia con este , debería ser bastante sencillo configurar el entrenamiento con su propia verdad básica.

Finalmente, si usar YOLO o CNN no es un objetivo en sí mismo, sino solo la solución, cualquiera de las "verdades básicas" anteriores podría usarse directamente como una solución, y no para entrenar un modelo.

Espero haber entendido bien tu pregunta


Si puede dar el código para el mismo, ya que esta pregunta contiene recompensa
Pulkit Bhatnagar

la tarea es, en última instancia, obtener el texto, pero primero estoy tratando de identificar todos los caracteres alfanuméricos que
contiene

Nada de lo que propuse es realmente una solución lista para usar, y creo que el código algorítmico no sería corto o simple, así que lo dejaré al nivel de ideas :-). ps gracias por el voto a favor!
Yuri Feldman
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.