Ermitteln Sie mit opencv die Position des gesamten im Bild vorhandenen Textes

11

Ich habe dieses Bild, das Text (Zahlen und Alphabete) enthält. Ich möchte die Position aller Texte und Zahlen in diesem Bild ermitteln. Außerdem möchte ich auch den gesamten Text extrahieren.

Geben Sie hier die Bildbeschreibung ein

Wie erhalte ich die Koordinaten sowie den gesamten Text (Zahlen und Alphabete) in meinem Bild? Zum Beispiel 10B, 44, 16, 38, 22B usw.

Pulkit Bhatnagar
quelle
Was ist deine Tensorflow-Version? Wenn Ihre Version 2.1 ist, versuchen Sie, 2.0
gellezzz
1
Es ist keine gute Praxis, Kopfgelder auf schlechte Fragen zu werfen. Sie haben keine Kenntnisse darüber gezeigt, wie dies zu tun ist. Es sieht also so aus, als würden Sie lediglich versuchen, Entwickler dazu zu bringen, eine Komplettlösung im Austausch für ein paar Wiederholungspunkte zu codieren. Ich erwarte aus diesem Grund keine perfekten Antworten, aber ich glaube, dass Sie auf freiberuflichen Websites bessere Lösungen finden können, wenn Sie die Leute für ihre Zeit bezahlen.
Karlphillip
@karlphillip tut mir so leid, aber ich bin ein Anfänger, ich brauche etwas, um anzufangen, oder? Können Sie mir dabei helfen
Pulkit Bhatnagar

Antworten:

13

Hier ist ein möglicher Ansatz, bei dem morphologische Operationen verwendet werden, um Nicht-Text-Konturen herauszufiltern. Die Idee ist:

  1. Erhalten Sie ein Binärbild. Laden Sie das Bild, Graustufen und dann Otsus Schwelle

  2. Entfernen Sie horizontale und vertikale Linien. Erstellen Sie horizontale und vertikale Kernel, indem Sie cv2.getStructuringElementLinien mit entfernencv2.drawContours

  3. Entfernen Sie diagonale Linien, Kreisobjekte und gekrümmte Konturen. Filtern Sie mithilfe des Konturbereichs cv2.contourArea und der Konturnäherung cv2.approxPolyDP , um Nicht-Text-Konturen zu isolieren

  4. Text-ROIs und OCR extrahieren. Finden Sie Konturen und filtern Sie mit Pytesseract nach ROIs und dann nach OCR .


Horizontale Linien entfernt, die grün hervorgehoben sind

Geben Sie hier die Bildbeschreibung ein

Vertikale Linien entfernt

Geben Sie hier die Bildbeschreibung ein

Verschiedene Nicht-Text-Konturen (diagonale Linien, kreisförmige Objekte und Kurven) wurden entfernt.

Geben Sie hier die Bildbeschreibung ein

Erkannte Textbereiche

Geben Sie hier die Bildbeschreibung ein

import cv2
import numpy as np
import pytesseract

pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.jpg')
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)

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:
    # Remove diagonal lines
    area = cv2.contourArea(c)
    if area < 100:
        cv2.drawContours(clean, [c], -1, 0, 3)
    # Remove circle objects
    elif area > 1000:
        cv2.drawContours(clean, [c], -1, 0, -1)
    # Remove curve stuff
    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()
nathancy
quelle
Gute Idee, diese Zeilen zuerst zu entfernen.
Karlphillip
schlechte Idee, auch Teile von Briefen zu entfernen ...
Walter Tross
8

Okay, hier ist eine andere mögliche Lösung. Ich weiß, dass Sie mit Python arbeiten - ich arbeite mit C ++. Ich gebe Ihnen einige Ideen und hoffe, dass Sie diese Antwort umsetzen können, wenn Sie dies wünschen.

Die Hauptidee ist, überhaupt keine Vorverarbeitung zu verwenden (zumindest nicht in der Anfangsphase) und sich stattdessen auf jedes Zielzeichen zu konzentrieren, einige Eigenschaften zu erhalten und jeden Blob nach diesen Eigenschaften zu filtern .

Ich versuche, die Vorverarbeitung nicht zu verwenden, weil: 1) Filter und morphologische Stadien die Qualität der Blobs beeinträchtigen können und 2) Ihre Ziel-Blobs einige Eigenschaften aufweisen, die wir ausnutzen könnten, hauptsächlich: Seitenverhältnis und Fläche .

Probieren Sie es aus, die Zahlen und Buchstaben scheinen alle größer als breiter zu sein. Außerdem scheinen sie innerhalb eines bestimmten Bereichswerts zu variieren. Sie möchten beispielsweise Objekte "zu breit" oder "zu groß" verwerfen .

Die Idee ist, dass ich alles filtern werde, was nicht in vorberechnete Werte fällt. Ich habe die Zeichen (Zahlen und Buchstaben) untersucht und dabei minimale, maximale Flächenwerte und ein minimales Seitenverhältnis (hier das Verhältnis zwischen Höhe und Breite) angegeben.

Lassen Sie uns am Algorithmus arbeiten. Lesen Sie zunächst das Bild und ändern Sie die Größe auf die Hälfte der Abmessungen. Ihr Bild ist viel zu groß. In Graustufen konvertieren und über otsu ein Binärbild erhalten, hier im Pseudocode:

//Read input:
inputImage = imread( "diagram.png" );

//Resize Image;
resizeScale = 0.5;

inputResized = imresize( inputImage, resizeScale );

//Convert to grayscale;
inputGray = rgb2gray( inputResized );

//Get binary image via otsu:
binaryImage = imbinarize( inputGray, "Otsu" );

Cool. Wir werden mit diesem Bild arbeiten. Sie müssen jeden weißen Fleck untersuchen und einen "Eigenschaftenfilter" anwenden . Ich verwende verbundene Komponenten mit Statistiken, um durch jeden Blob zu schleifen und dessen Fläche und Seitenverhältnis zu ermitteln. In C ++ geschieht dies wie folgt:

//Prepare the output matrices:
cv::Mat outputLabels, stats, centroids;
int connectivity = 8;

//Run the binary image through connected components:
int numberofComponents = cv::connectedComponentsWithStats( binaryImage, outputLabels, stats, centroids, connectivity );

//Prepare a vector of colors  color the filtered blobs in black
std::vector<cv::Vec3b> colors(numberofComponents+1);
colors[0] = cv::Vec3b( 0, 0, 0 ); // Element 0 is the background, which remains black.

//loop through the detected blobs:
for( int i = 1; i <= numberofComponents; i++ ) {

    //get area:
    auto blobArea = stats.at<int>(i, cv::CC_STAT_AREA);

    //get height, width and compute aspect ratio:
    auto blobWidth = stats.at<int>(i, cv::CC_STAT_WIDTH);
    auto blobHeight = stats.at<int>(i, cv::CC_STAT_HEIGHT);
    float blobAspectRatio = (float)blobHeight/(float)blobWidth;

    //Filter your blobs

};

Jetzt wenden wir den Eigenschaftenfilter an. Dies ist nur ein Vergleich mit den vorberechneten Schwellenwerten. Ich habe folgende Werte verwendet:

Minimum Area: 40  Maximum Area:400
MinimumAspectRatio:  1

forVergleichen Sie in Ihrer Schleife die aktuellen Blob-Eigenschaften mit diesen Werten. Wenn die Tests positiv sind, "malen" Sie den Klecks schwarz. Fortsetzung innerhalb der forSchleife:

    //Filter your blobs

    //Test the current properties against the thresholds:
    bool areaTest =  (blobArea > maxArea)||(blobArea < minArea);
    bool aspectRatioTest = !(blobAspectRatio > minAspectRatio); //notice we are looking for TALL elements!

    //Paint the blob black:
    if( areaTest || aspectRatioTest ){
        //filtered blobs are colored in black:
        colors[i] = cv::Vec3b( 0, 0, 0 );
    }else{
        //unfiltered blobs are colored in white:
        colors[i] = cv::Vec3b( 255, 255, 255 );
    }

Erstellen Sie nach der Schleife das gefilterte Bild:

cv::Mat filteredMat = cv::Mat::zeros( binaryImage.size(), CV_8UC3 );
for( int y = 0; y < filteredMat.rows; y++ ){
    for( int x = 0; x < filteredMat.cols; x++ )
    {
        int label = outputLabels.at<int>(y, x);
        filteredMat.at<cv::Vec3b>(y, x) = colors[label];
    }
}

Und ... das war's auch schon. Sie haben alle Elemente gefiltert, die nicht dem entsprechen, wonach Sie suchen. Wenn Sie den Algorithmus ausführen, erhalten Sie folgendes Ergebnis:

Geben Sie hier die Bildbeschreibung ein

Ich habe zusätzlich die Begrenzungsrahmen der Blobs gefunden, um die Ergebnisse besser zu visualisieren:

Geben Sie hier die Bildbeschreibung ein

Wie Sie sehen, werden einige Elemente falsch erkannt. Sie können den "Eigenschaftenfilter" verfeinern, um die gesuchten Zeichen besser zu identifizieren. Eine tiefere Lösung, die ein wenig maschinelles Lernen erfordert, erfordert die Konstruktion eines "idealen Merkmalsvektors", das Extrahieren von Merkmalen aus den Blobs und das Vergleichen beider Vektoren über ein Ähnlichkeitsmaß. Sie können auch eine Nachbearbeitung anwenden , um die Ergebnisse zu verbessern ...

Was auch immer, Mann, Ihr Problem ist weder trivial noch einfach skalierbar, und ich gebe Ihnen nur Ideen. Hoffentlich können Sie Ihre Lösung implementieren.

eldesgraciado
quelle
Jede Chance, dass Sie dasselbe Programm in Python konvertieren können
Pulkit Bhatnagar
@PulkitBhatnagar Ja, natürlich. Halten Sie sich einfach fest, ich habe in ein paar Minuten einen perfekten Port bereit.
eldesgraciado
??
Hast
Ah ja. Es tut mir schrecklich leid, mein Herr, ich hatte einige Probleme, aber die Umstellung kommt gut voran. Warte einfach. Danke.
eldesgraciado
Ich hätte nie gedacht, dass das ein Sarkasmus sein könnte.
Pulkit Bhatnagar
4

Eine Methode ist die Verwendung eines Schiebefensters (es ist teuer).

Bestimmen Sie die Größe der Zeichen im Bild (alle Zeichen haben dieselbe Größe wie im Bild) und legen Sie die Größe des Fensters fest. Versuchen Sie es mit tesseract zur Erkennung (Das Eingabebild muss vorverarbeitet werden). Wenn ein Fenster Zeichen nacheinander erkennt, speichern Sie die Koordinaten des Fensters. Füge die Koordinaten zusammen und erhalte die Region auf den Zeichen.

rette die Erde
quelle
Ich denke, 100 Kopfgeld ist für die Antwort
Himanshu Poddar