Verwenden von YOLO oder anderen Bilderkennungstechniken, um den gesamten in Bildern vorhandenen alphanumerischen Text zu identifizieren

12

Ich habe ein Diagramm mit mehreren Bildern, die alle Beschriftungen als alphanumerische Zeichen anstelle der Textbeschriftung selbst enthalten. Ich möchte, dass mein YOLO-Modell alle darin enthaltenen Zahlen und alphanumerischen Zeichen identifiziert.

Wie kann ich mein YOLO-Modell trainieren, um dasselbe zu tun? Den Datensatz finden Sie hier. https://drive.google.com/open?id=1iEkGcreFaBIJqUdAADDXJbUrSj99bvoi

Zum Beispiel: siehe die Begrenzungsrahmen. Ich möchte, dass YOLO erkennt, wo immer der Text vorhanden ist. Derzeit ist es jedoch nicht erforderlich, den darin enthaltenen Text zu identifizieren.

Geben Sie hier die Bildbeschreibung ein

Dasselbe muss auch für diese Art von Bildern getan werden Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

Die Bilder können hier heruntergeladen werden

Dies ist, was ich versucht habe, opencv zu verwenden, aber es funktioniert nicht für alle Bilder im Datensatz.

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

Gibt es ein Modell oder eine OpenCV-Technik oder ein vorab trainiertes Modell, das dasselbe für mich tun kann? Ich brauche nur die Begrenzungsrahmen um alle alphanumerischen Zeichen in den Bildern. Danach muss ich identifizieren, was darin vorhanden ist. Der zweite Teil ist derzeit jedoch nicht wichtig.

Pulkit Bhatnagar
quelle
Beantwortet das deine Frage? OpenCV! _Src.empty () in Funktion 'cvtColor' Fehler
Amit Yadav
1
Schauen Sie sich mit opencv
nathancy
das funktioniert nicht für alle Bilder
Pulkit Bhatnagar

Antworten:

7

Ein möglicher Ansatz ist die Verwendung des Deep-Learning-Textdetektors EAST (Efficient and Accurate Scene Text), der auf dem 2017 erschienenen Artikel EAST: A Efficient and Accurate Scene Text Detector von Zhou et al. Basiert . Das Modell wurde ursprünglich für die Erkennung von Text in natürlichen Szenenbildern trainiert, kann jedoch möglicherweise auf Diagrammbilder angewendet werden. EAST ist ziemlich robust und kann unscharfen oder reflektierenden Text erkennen. Hier ist eine modifizierte Version von Adrian Rosebrocks Implementierung von EAST. Anstatt den Textdetektor direkt auf das Bild anzuwenden, können wir versuchen, möglichst viele Nicht-Textobjekte auf dem Bild zu entfernen, bevor die Texterkennung durchgeführt wird. Die Idee ist, horizontale Linien, vertikale Linien und Nicht-Text-Konturen (Kurven, Diagonalen, Kreisformen) zu entfernen, bevor die Erkennung angewendet wird. Hier sind die Ergebnisse mit einigen Ihrer Bilder:

Geben Sie ->Nicht-Text-Konturen in Grün ein

Ergebnis

Andere Bilder

Das vorgeübte frozen_east_text_detection.pb für die Texterkennung erforderliche Modell finden Sie hier . Obwohl das Modell den größten Teil des Textes erfasst, sind die Ergebnisse nicht 100% genau und weisen gelegentlich falsch positive Ergebnisse auf, wahrscheinlich aufgrund der Art und Weise, wie es auf natürlichen Szenenbildern trainiert wurde. Um genauere Ergebnisse zu erhalten, müssten Sie wahrscheinlich Ihr eigenes benutzerdefiniertes Modell trainieren. Wenn Sie jedoch eine anständige Out-of-the-Box-Lösung wünschen, sollte dies funktionieren. Weitere Informationen zum EAST-Textdetektor finden Sie im Blogbeitrag zu OpenCV Text Detection (EAST Text Detector) von Adrian .

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
quelle
Sehr vollständige Antwort. Wie viele Stunden Mühe?
Karlphillip
Etwa eine Stunde und weitere 30 Minuten, um es
aufzuschreiben
Bis heute bin ich immer noch überrascht, wie viele Menschen innerhalb weniger Tage mit äußerst ähnlichen Fragen zum Lebenslauf auftauchen. Es sieht fast so aus, als würden Leute aus derselben Bildverarbeitungsklasse nach Hilfe suchen, um ihre Hausaufgaben zu erledigen, oder nur jemanden suchen, der die Hausaufgaben für sie erledigt. Es ist ein wirklich bizarrer "Zufall".
Karlphillip
2
@karlphillip Vielleicht kommt mir diese Frage bekannt vor, weil das OP sie vor ungefähr einer Woche gepostet hat. Er möchte so ziemlich eine STRG + C-, STRG + V-Antwort, die alle seine Fälle sofort abdeckt. Ich denke, Sie werden diese Frage vielleicht in ein paar Wochen wieder sehen!
eldesgraciado
3
@eldesgraciado Ich habe gerade festgestellt, dass OP vor einigen Wochen eine ähnliche Frage gestellt hat. Ich wusste nicht einmal, dass es bis jetzt dieselbe Person war! Ich habe mich auch gefragt, warum die Frage sehr vertraut aussah
Nathancy
6

Der Einfachheit halber möchte ich das Paket keras_ocr hinzufügen . Es kann einfach mit pip installiert werden und basiert auf dem CRAFT-Textdetektor, der etwas neuer als der EAST-Detektor ist, wenn ich mich nicht irre.

Neben der Erkennung werden bereits einige OCR-Vorgänge ausgeführt! Die Ergebnisse sind wie folgt: Sehen Sie dies als Alternative, die möglicherweise einfacher zu implementieren ist, als die akzeptierte Antwort.Geben Sie hier die Bildbeschreibung ein

Victor Sonck
quelle
Hallo Sieger, funktioniert es für mindestens 70% meiner Bilder?
Pulkit Bhatnagar
Sie haben keine Beschriftungen in Ihren Datensatz aufgenommen. Daher kann ich Ihnen nicht wirklich sagen, wie viel Prozent der Bilder bearbeitet werden, wenn ich nicht durch Vergleich mit einem Etikett überprüfen kann, ob es funktioniert hat. Da es sich jedoch um ein Pip-Paket handelt, sollte es für Sie einfach genug sein, es in Ihrem Datensatz auszuführen und sich selbst davon zu überzeugen :)
Victor Sonck,
4

Was Sie beschreiben, scheint OCR ( Optical Character Recognition ) zu sein. Eine OCR-Engine, die ich kenne, ist Tesseract , obwohl es auch diese von IBM und anderen gibt.

Da YOLO ursprünglich für eine ganz andere Aufgabe geschult wurde, muss es für die Lokalisierung von Text wahrscheinlich von Grund auf neu trainiert werden. Man könnte versuchen, vorhandene Pakete (angepasst an Ihre spezifische Einstellung) für die Grundwahrheit zu verwenden (obwohl es sich zu beachten lohnt, dass das Modell im Allgemeinen höchstens so gut ist wie die Grundwahrheit). Oder generieren Sie möglicherweise einfacher synthetische Daten für das Training (dh fügen Sie Text an Positionen hinzu, die Sie zu vorhandenen Zeichnungen auswählen, und trainieren Sie dann, um ihn zu lokalisieren).

Wenn alle Ihre Zielbilder ähnlich wie oben strukturiert sind, können Sie alternativ versuchen, mithilfe der klassischen CV-Heuristik wie oben eine Grundwahrheit zu erstellen, um Symbole zu trennen / zu segmentieren, gefolgt von einer Klassifizierung mithilfe eines auf MNIST trainierten CNN oder ähnlichem, um dies zu bestimmen wenn ein bestimmter Blob ein Symbol enthält.

Für den Fall, dass Sie sich für YOLO entscheiden - es gibt bereits Implementierungen in Python, z. B. hatte ich einige Erfahrungen mit dieser -, sollte es ziemlich einfach sein, ein Training mit Ihrer eigenen Grundwahrheit einzurichten.

Wenn die Verwendung von YOLO oder CNN kein Ziel an sich ist, sondern nur die Lösung, kann jede der oben genannten "Grundwahrheiten" direkt als Lösung verwendet werden und nicht zum Trainieren eines Modells.

Hoffe ich habe deine Frage richtig verstanden

Yuri Feldman
quelle
Wenn Sie den Code für das gleiche geben können, da diese Frage Kopfgeld enthält
Pulkit Bhatnagar
Die Aufgabe besteht darin, den Text letztendlich zu erhalten, aber ich versuche zuerst, alle darin enthaltenen alphanumerischen Zeichen zu identifizieren und dann OCR für dasselbe zu verwenden, sobald es identifiziert wurde
Pulkit Bhatnagar,
Nichts von dem, was ich vorgeschlagen habe, ist wirklich eine Out-of-the-Box-Lösung, und algorithmischer Code wäre meiner Meinung nach nicht kurz oder einfach, also lasse ich ihn auf der Ebene der Ideen :-). ps danke für das upvote!
Yuri Feldman