Utilisation de YOLO ou d'autres techniques de reconnaissance d'image pour identifier tout le texte alphanumérique présent dans les images

12

J'ai plusieurs diagrammes d'images, qui contiennent tous des étiquettes sous forme de caractères alphanumériques au lieu de simplement l'étiquette de texte elle-même. Je veux que mon modèle YOLO identifie tous les chiffres et caractères alphanumériques qu'il contient.

Comment puis-je entraîner mon modèle YOLO à faire de même. L'ensemble de données peut être trouvé ici. https://drive.google.com/open?id=1iEkGcreFaBIJqUdAADDXJbUrSj99bvoi

Par exemple: voir les boîtes englobantes. Je veux que YOLO détecte où le texte est présent. Cependant, il n'est actuellement pas nécessaire d'identifier le texte qu'il contient.

entrez la description de l'image ici

De même, la même chose doit être faite pour ce type d'images entrez la description de l'image ici entrez la description de l'image ici

Les images peuvent être téléchargées ici

C'est ce que j'ai essayé d'utiliser opencv mais cela ne fonctionne pas pour toutes les images du jeu de données.

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

Y a-t-il un modèle ou une technique d'opencv ou un modèle pré-formé qui peut faire la même chose pour moi? J'ai juste besoin des cadres de délimitation autour de tous les caractères alphanumériques présents dans les images. Après cela, je dois identifier ce qui s'y trouve. Cependant la deuxième partie n'est pas importante actuellement.

Pulkit Bhatnagar
la source
Est-ce que cela répond à votre question? OpenCV! _Src.empty () dans la fonction 'cvtColor' erreur
Amit Yadav
cela ne fonctionne pas pour toutes les images
Pulkit Bhatnagar

Réponses:

7

Une approche possible consiste à utiliser le détecteur de texte d'apprentissage en profondeur EAST (Efficient and Accurate Scene Text) basé sur l'article 2017 de Zhou et al., EAST: An Efficient and Accurate Scene Text Detector . Le modèle a été initialement formé pour détecter du texte dans des images de scènes naturelles, mais il peut être possible de l'appliquer sur des images de diagramme. EAST est assez robuste et est capable de détecter du texte flou ou réfléchissant. Voici une version modifiée de la mise en œuvre d'Adrian Rosebrock d'EAST. Au lieu d'appliquer le détecteur de texte directement sur l'image, nous pouvons essayer de supprimer autant d'objets non textuels sur l'image avant d'effectuer la détection de texte. L'idée est de supprimer les lignes horizontales, les lignes verticales et les contours non textuels (courbes, diagonales, formes circulaires) avant d'appliquer la détection. Voici les résultats avec certaines de vos images:

Saisie ->des contours non textuels à supprimer en vert

Résultat

Autres images

Le frozen_east_text_detection.pbmodèle pré-formé nécessaire pour effectuer la détection de texte peut être trouvé ici . Bien que le modèle capture la plupart du texte, les résultats ne sont pas précis à 100% et présentent parfois des faux positifs, probablement en raison de la façon dont il a été formé sur les images de scènes naturelles. Pour obtenir des résultats plus précis, vous devrez probablement former votre propre modèle personnalisé. Mais si vous voulez une solution décente prête à l'emploi, cela devrait vous fonctionner. Consultez le billet de blog Adrian OpenCV Text Detection (détecteur de texte EAST) pour une explication plus complète du détecteur de texte EAST.

Code

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
la source
Réponse très complète. Combien d'heures d'effort?
karlphillip
Environ une heure et 30 minutes
supplémentaires
À ce jour, je suis toujours surpris du nombre de personnes qui se présentent avec des questions de CV extrêmement similaires en quelques jours. On dirait presque que des gars de la même classe de traitement d'image cherchent de l'aide pour terminer leurs devoirs ou cherchent simplement quelqu'un pour faire les devoirs pour eux. C'est une "coïncidence" vraiment bizarre.
karlphillip
2
@karlphillip Peut-être que cette question vous semble familière, car le PO l'a publiée il y a environ une semaine. Il veut à peu près une réponse CTRL + C, CTRL + V qui couvre tous ses cas dès la sortie de la boîte, donc, je suppose que vous pourriez revoir cette même question dans quelques semaines!
eldesgraciado
3
@eldesgraciado Je viens de réaliser que OP a posté une question similaire il y a quelques semaines. Je n'avais même pas réalisé que c'était la même personne jusqu'à maintenant! Je me demandais aussi pourquoi la question semblait si familière
nathancy
6

Par souci de commodité, j'aimerais ajouter le package keras_ocr . Il peut facilement être installé avec pip et est basé sur le détecteur de texte CRAFT, qui est un peu plus récent que le détecteur EAST si je ne me trompe pas.

À côté de la détection, il fait déjà aussi de l'OCR! Les résultats sont les suivants, voir cela comme une alternative, peut-être plus facile à mettre en œuvre, que la réponse acceptée.entrez la description de l'image ici

Victor Sonck
la source
Salut vainqueur, ça marche pour au moins 70% de mes images?
Pulkit Bhatnagar
Vous n'avez pas inclus d'étiquettes dans votre jeu de données. Je ne peux donc pas vraiment vous dire combien de% des images il fonctionne, si je n'ai pas de moyen de vérifier si cela a fonctionné en le comparant à une étiquette. Cependant, c'est un paquet pip, il devrait donc être assez facile pour vous de l'exécuter sur votre jeu de données et de voir par vous-même :)
Victor Sonck
4

Ce que vous décrivez semble être l'OCR (reconnaissance optique des caractères ). Un moteur OCR que je connais est tesseract , bien qu'il y ait aussi celui d'IBM et d'autres.

Comme YOLO a été initialement formé pour une tâche très différente, son utilisation pour localiser du texte nécessitera probablement de le recycler à partir de zéro. On pourrait essayer d'utiliser des packages existants (adaptés à votre environnement spécifique) pour la vérité terrain (bien qu'il soit bon de se rappeler que le modèle ne serait généralement au mieux aussi bon que la vérité terrain). Ou, peut-être plus facilement, générez des données synthétiques pour la formation (c.-à-d. Ajoutez du texte aux positions que vous choisissez aux dessins existants puis entraînez-vous à le localiser).

Alternativement, si toutes vos images cibles sont structurées de la même manière que ci-dessus, on pourrait essayer de créer une vérité fondamentale en utilisant l'heuristique CV classique comme vous l'avez fait ci-dessus pour séparer / segmenter les symboles, suivie d'une classification en utilisant un CNN formé sur MNIST ou similaire pour déterminer si un blob donné contient un symbole.

Dans le cas où vous optez pour YOLO - il existe des implémentations existantes en python, par exemple, j'ai eu une certaine expérience avec celui-ci - devrait être assez simple pour configurer la formation avec votre propre vérité de terrain.

Enfin, si l'utilisation de YOLO ou CNN n'est pas un objectif en soi mais plutôt seulement la solution, n'importe laquelle des «vérités fondamentales» ci-dessus pourrait être utilisée directement comme solution, et non pour former un modèle.

J'espère que j'ai bien compris votre question

Yuri Feldman
la source
Si vous pouvez donner le code pour la même chose, car cette question contient des primes
Pulkit Bhatnagar
la tâche consiste à obtenir le texte, mais j'essaie d'abord d'identifier tous les caractères alphanumériques, puis d'utiliser l'OCR pour le même une fois identifié
Pulkit Bhatnagar
Rien de ce que j'ai proposé n'est vraiment une solution prête à l'emploi, et le code algorithmique ne serait ni court ni simple je pense, donc je vais le laisser au niveau des idées :-). ps merci pour le vote positif!
Yuri Feldman