Utilizzando YOLO o altre tecniche di riconoscimento delle immagini per identificare tutto il testo alfanumerico presente nelle immagini


12

Ho un diagramma di immagini multiple, tutte contenenti etichette come caratteri alfanumerici anziché solo l'etichetta di testo stessa. Voglio che il mio modello YOLO identifichi tutti i numeri e i caratteri alfanumerici presenti in esso.

Come posso addestrare il mio modello YOLO a fare lo stesso. Il set di dati è disponibile qui. https://drive.google.com/open?id=1iEkGcreFaBIJqUdAADDXJbUrSj99bvoi

Ad esempio: vedere le caselle di delimitazione. Voglio che YOLO rilevi ovunque sia presente il testo. Tuttavia attualmente non è necessario identificare il testo al suo interno.

inserisci qui la descrizione dell'immagine

Lo stesso deve essere fatto per questo tipo di immagini inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Le immagini possono essere scaricate qui

Questo è quello che ho provato ad usare Opencv ma non funziona per tutte le immagini nel set di dati.

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

C'è qualche modello o qualche tecnica a cielo aperto o qualche modello pre-addestrato che può fare lo stesso per me? Ho solo bisogno delle caselle di delimitazione attorno a tutti i caratteri alfanumerici presenti nelle immagini. Dopodiché devo identificare ciò che è presente in esso. Tuttavia, la seconda parte non è importante al momento.




che non funziona per tutte le immagini
Pulkit Bhatnagar,

Risposte:


7

Un possibile approccio consiste nell'utilizzare il rilevatore di testi per l'apprendimento approfondito EAST (Efficient and Accurate Scene Text) basato sul documento di Zhou et al., EAST: un rivelatore di testi per scene efficienti e accurati . Il modello è stato originariamente addestrato per rilevare il testo nelle immagini di scene naturali, ma potrebbe essere possibile applicarlo alle immagini dei diagrammi. EAST è abbastanza robusto ed è in grado di rilevare testi sfocati o riflettenti. Ecco una versione modificata dell'implementazione di EAST di Adrian Rosebrock. Invece di applicare il rilevatore di testo direttamente sull'immagine, possiamo provare a rimuovere quanti più oggetti non di testo sull'immagine prima di eseguire il rilevamento del testo. L'idea è di rimuovere le linee orizzontali, le linee verticali e i contorni non testuali (curve, diagonali, forme circolari) prima di applicare il rilevamento. Ecco i risultati con alcune delle tue immagini:

Immettere ->contorni non testuali da rimuovere in verde

Risultato

Altre immagini

Il frozen_east_text_detection.pbmodello preinstallato necessario per eseguire il rilevamento del testo è disponibile qui . Sebbene il modello catturi la maggior parte del testo, i risultati non sono accurati al 100% e hanno occasionalmente falsi positivi probabilmente a causa di come è stato addestrato sulle immagini di scene naturali. Per ottenere risultati più precisi, probabilmente dovrai addestrare il tuo modello personalizzato. Ma se vuoi una soluzione decente pronta all'uso, questo dovrebbe funzionare. Dai un'occhiata al post sul blog di Adrian OpenCV Text Detection (EAST text detector) per una spiegazione più completa del rilevatore di testo EAST.

Codice

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

Risposta molto completa. Quante ore di sforzo?
karlphillip,

Circa un'ora e altri 30 minuti per scriverlo
nathancy

Ad oggi sono ancora sorpreso dal numero di persone che si presentano con domande CV estremamente simili nel giro di pochi giorni. Sembra quasi che i ragazzi della stessa classe di elaborazione delle immagini stiano cercando aiuto per finire i compiti o semplicemente cercando qualcuno che faccia i compiti per loro. È una "coincidenza" davvero bizzarra.
karlphillip,

2
@karlphillip Forse questa domanda sembra familiare perché l'OP l'ha pubblicata circa una settimana fa. Vuole praticamente una risposta CTRL + C, CTRL + V che copra tutti i suoi casi immediatamente, quindi, immagino che potresti vedere di nuovo la stessa domanda tra un paio di settimane!
eldesgraciado,

3
@eldesgraciado Mi sono appena reso conto che OP ha pubblicato una domanda simile qualche settimana fa. Fino ad ora non avevo nemmeno realizzato che fosse la stessa persona! Mi chiedevo anche perché la domanda fosse molto familiare
nathancy il

6

Per comodità, vorrei aggiungere il pacchetto keras_ocr . Può essere facilmente installato con pip ed è basato sul rivelatore di testo CRAFT, che è un po 'più nuovo del rivelatore EAST se non sbaglio.

Accanto al rilevamento fa già anche un po 'di OCR! I risultati sono come di seguito, vedere questo come un'alternativa, forse più facile da implementare, rispetto alla risposta accettata.inserisci qui la descrizione dell'immagine


Ciao vincitore, funziona per almeno il 70% delle mie immagini?
Pulkit Bhatnagar,

Non hai incluso etichette nel tuo set di dati. Quindi non posso davvero dirti su quale% delle immagini funziona, se non ho modo di verificare se ha funzionato confrontandolo con un'etichetta. Tuttavia, è un pacchetto pip, quindi dovrebbe essere abbastanza facile per te eseguirlo sul tuo set di dati e vederlo di persona :)
Victor Sonck,

4

Quello che stai descrivendo sembra essere OCR (riconoscimento ottico dei caratteri ). Un motore OCR che conosco è tesseract , sebbene ce ne sia anche uno di IBM e altri.

Poiché YOLO era stato originariamente addestrato per un compito molto diverso, per usarlo per localizzare il testo sarà probabilmente necessario riqualificarlo da zero. Si potrebbe provare a usare i pacchetti esistenti (adattati alle proprie impostazioni specifiche) per la verità di base (anche se vale la pena ricordare che il modello sarebbe generalmente buono al massimo quanto la verità di base). O, forse più facilmente, genera dati sintetici per l'addestramento (ad esempio aggiungi testo nelle posizioni che scegli ai disegni esistenti e poi ti alleni per localizzarlo).

In alternativa, se tutte le immagini di destinazione sono strutturate in modo simile a quanto sopra, si potrebbe provare a creare la verità di base usando l'euristica CV classica come sopra fatto per separare / segmentare i simboli, seguita dalla classificazione usando una CNN addestrata su MNIST o simile per determinare se un determinato BLOB contiene un simbolo.

Nel caso in cui tu opti per YOLO - ci sono implementazioni esistenti in Python, ad esempio ho avuto qualche esperienza con questo - dovrebbe essere abbastanza semplice per impostare l'allenamento con la tua verità di base.

Infine, se l'uso di YOLO o CNN non è un obiettivo in sé, ma piuttosto solo la soluzione, una qualsiasi delle "verità fondamentali" di cui sopra potrebbe essere utilizzata direttamente come soluzione e non per formare un modello.

Spero di aver capito bene la tua domanda


Se riesci a fornire il codice per lo stesso, poiché questa domanda contiene generosità
Pulkit Bhatnagar,

il compito è in definitiva ottenere il testo, ma prima cerco di identificare tutti i caratteri alfanumerici in esso contenuti, quindi uso l'OCR per lo stesso identificato una volta
Pulkit Bhatnagar

Nulla di ciò che ho proposto è davvero una soluzione pronta all'uso, e penso che il codice algoritmico non sia breve o semplice, quindi lo lascerò a livello di idee :-). ps grazie per l'upgrade!
Yuri Feldman,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.