webentwicklung-frage-antwort-db.com.de

Algorithmus zum Erkennen von Papierbogenecken im Foto

Wie lassen sich die Ecken einer Rechnung/Quittung/eines Blattes Papier auf einem Foto am besten erkennen? Dies ist für die spätere Perspektivenkorrektur vor der OCR zu verwenden.

Mein aktueller Ansatz war:

RGB> Grau> Erkennung fehlerhafter Kanten mit Schwellenwert> Erweitern (1)> Kleine Objekte entfernen (6)> Grenzobjekte löschen> Größeres Blog basierend auf konvexem Bereich auswählen. > [Eckenerkennung - Nicht implementiert]

Ich kann nicht anders, als zu glauben, dass es einen robusteren „intelligenten“/statistischen Ansatz geben muss, um diese Art der Segmentierung zu handhaben. Ich habe nicht viele Trainingsbeispiele, aber ich könnte wahrscheinlich 100 Bilder zusammen bekommen.

Breiteren Kontext:

Ich verwende matlab als Prototyp und plane die Implementierung des Systems in OpenCV und Tesserect-OCR. Dies ist das erste von mehreren Bildverarbeitungsproblemen, die ich für diese spezielle Anwendung lösen muss. Ich möchte meine eigene Lösung entwickeln und mich mit den Bildverarbeitungsalgorithmen vertraut machen.

Hier sind einige Beispiele, mit denen der Algorithmus umgehen soll: Wenn Sie die Herausforderung annehmen möchten, finden Sie die großen Bilder unter http://madteckhead.com/tmp

case 1
(Quelle: madteckhead.com )

case 2
(Quelle: madteckhead.com )

case 3
(Quelle: madteckhead.com )

case 4
(Quelle: madteckhead.com )

Im besten Fall ergibt dies:

case 1 - canny
(Quelle: madteckhead.com )

case 1 - post canny
(Quelle: madteckhead.com )

case 1 - largest blog
(Quelle: madteckhead.com )

In anderen Fällen schlägt es jedoch leicht fehl:

case 2 - canny
(Quelle: madteckhead.com )

case 2 - post canny
(Quelle: madteckhead.com )

case 2 - largest blog
(Quelle: madteckhead.com )

Vielen Dank im Voraus für all die tollen Ideen! Ich liebe so!

BEARBEITEN: Hough Transform Progress

F: Welcher Algorithmus würde die Hough-Linien gruppieren, um Ecken zu finden? Den Ratschlägen aus den Antworten folgend, konnte ich die Hough-Transformation verwenden, Linien auswählen und sie filtern. Mein gegenwärtiger Ansatz ist ziemlich grob. Ich habe die Annahme gemacht, dass die Rechnung immer weniger als 15 Grad vom Bild abweicht. Wenn dies der Fall ist, erhalte ich vernünftige Ergebnisse für Zeilen (siehe unten). Ich bin mir jedoch nicht ganz sicher, welchen Algorithmus es gibt, um die Linien zu gruppieren (oder abzustimmen), um sie für die Ecken zu extrapolieren. Die Hough-Linien sind nicht durchgehend. Bei verrauschten Bildern kann es parallele Linien geben, sodass eine Form oder ein Abstand von den Linienursprungsmetriken erforderlich sind. Irgendwelche Ideen?

case 1 case 2case 3case 4
(Quelle: madteckhead.com )

90
Nathan Keller

Ich bin Martins Freund, der Anfang dieses Jahres daran gearbeitet hat. Dies war mein erstes Programmierprojekt und endete irgendwie in einem Rausch, also braucht der Code ein paar Fehler ... Dekodierung ... Ich gebe ein paar Tipps von dem, was ich Sie schon gesehen habe, und dann sortiere meinen Code an meinem freien Tag morgen.

Erster Tipp: OpenCV und python sind fantastisch. Wechseln Sie so schnell wie möglich zu ihnen. : D

Anstatt kleine Gegenstände und/oder Geräusche zu entfernen, senken Sie die Fesseln, damit mehr Kanten aufgenommen werden, und suchen Sie dann die größte geschlossene Kontur (in OpenCV verwenden Sie findcontour() mit einigen einfachen Parametern, ich glaube, ich habe CV_RETR_LIST). könnte immer noch Probleme haben, wenn es auf einem weißen Blatt Papier ist, aber es lieferte definitiv die besten Ergebnisse.

Versuchen Sie für die Houghline2() -Transformation mit dem CV_HOUGH_STANDARD Im Gegensatz zum CV_HOUGH_PROBABILISTIC, Es wird rho und theta Werte, die die Linie in Polarkoordinaten definieren, und dann können Sie die Linien innerhalb einer bestimmten Toleranz zu diesen gruppieren.

Meine Gruppierung diente als Nachschlagetabelle. Für jede Zeile, die von der Hough-Transformation ausgegeben wurde, ergab sich ein Rho-Theta-Paar. Wenn diese Werte innerhalb von beispielsweise 5% eines Wertepaares in der Tabelle lagen, wurden sie verworfen, und wenn sie außerhalb dieser 5% lagen, wurde ein neuer Eintrag zur Tabelle hinzugefügt.

Sie können dann viel einfacher parallele Linien oder Abstände zwischen Linien analysieren.

Hoffe das hilft.

26
Daniel Crowley

Eine Studentengruppe an meiner Universität hat kürzlich eine iPhone-App (und python OpenCV-App)) demonstriert, für die sie genau dies geschrieben hatten.

  • Medianfilter zum vollständigen Entfernen des Textes auf dem Papier (dies war handgeschriebener Text auf weißem Papier mit ziemlich guter Beleuchtung und funktioniert möglicherweise nicht mit gedrucktem Text, es hat sehr gut funktioniert). Der Grund war, dass es die Eckenerkennung viel einfacher macht.
  • Hough Transformiere für Linien
  • Finden Sie die Peaks im Hough Transform-Speicherplatz und zeichnen Sie jede Linie über das gesamte Bild.
  • Analysieren Sie die Linien und entfernen Sie alle sehr nahe beieinander liegenden und in einem ähnlichen Winkel stehenden Linien (gruppieren Sie die Linien zu einer). Dies ist notwendig, da die Hough-Transformation nicht perfekt ist, da sie in einem diskreten Sample-Raum arbeitet.
  • Finden Sie Paare von Linien, die ungefähr parallel sind und andere Paare schneiden, um zu sehen, welche Linien Quads bilden.

Dies schien ziemlich gut zu funktionieren und sie waren in der Lage, ein Foto von einem Stück Papier oder Buch zu machen, die Eckenerkennung durchzuführen und dann das Dokument im Bild in fast Echtzeit auf eine flache Ebene abzubilden (es gab eine einzige OpenCV-Funktion, die ausgeführt werden konnte das Mapping). Es gab keine OCR, als ich sah, dass es funktionierte.

17
Martin Foot

Folgendes habe ich mir nach einigem Experimentieren ausgedacht:

import cv, cv2, numpy as np
import sys

def get_new(old):
    new = np.ones(old.shape, np.uint8)
    cv2.bitwise_not(new,new)
    return new

if __== '__main__':
    orig = cv2.imread(sys.argv[1])

    # these constants are carefully picked
    MORPH = 9
    CANNY = 84
    HOUGH = 25

    img = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
    cv2.GaussianBlur(img, (3,3), 0, img)


    # this is to recognize white on white
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(MORPH,MORPH))
    dilated = cv2.dilate(img, kernel)

    edges = cv2.Canny(dilated, 0, CANNY, apertureSize=3)

    lines = cv2.HoughLinesP(edges, 1,  3.14/180, HOUGH)
    for line in lines[0]:
         cv2.line(edges, (line[0], line[1]), (line[2], line[3]),
                         (255,0,0), 2, 8)

    # finding contours
    contours, _ = cv2.findContours(edges.copy(), cv.CV_RETR_EXTERNAL,
                                   cv.CV_CHAIN_APPROX_TC89_KCOS)
    contours = filter(lambda cont: cv2.arcLength(cont, False) > 100, contours)
    contours = filter(lambda cont: cv2.contourArea(cont) > 10000, contours)

    # simplify contours down to polygons
    rects = []
    for cont in contours:
        rect = cv2.approxPolyDP(cont, 40, True).copy().reshape(-1, 2)
        rects.append(rect)

    # that's basically it
    cv2.drawContours(orig, rects,-1,(0,255,0),1)

    # show only contours
    new = get_new(img)
    cv2.drawContours(new, rects,-1,(0,255,0),1)
    cv2.GaussianBlur(new, (9,9), 0, new)
    new = cv2.Canny(new, 0, CANNY, apertureSize=3)

    cv2.namedWindow('result', cv2.WINDOW_NORMAL)
    cv2.imshow('result', orig)
    cv2.waitKey(0)
    cv2.imshow('result', dilated)
    cv2.waitKey(0)
    cv2.imshow('result', edges)
    cv2.waitKey(0)
    cv2.imshow('result', new)
    cv2.waitKey(0)

    cv2.destroyAllWindows()

Nicht perfekt, aber funktioniert zumindest für alle Proben:

1234

16
Vanuan

Anstatt von der Kantenerkennung auszugehen, können Sie auch die Eckenerkennung verwenden.

Marvin Framework bietet zu diesem Zweck eine Implementierung des Moravec-Algorithmus. Sie könnten die Ecken der Papiere als Ausgangspunkt finden. Unterhalb der Ausgabe von Moravecs Algorithmus:

enter image description here

Sie können auch MSER (Maximal stabile Extremalbereiche) über dem Sobel-Operator result verwenden, um die stabilen Bereiche des Bildes zu finden. Für jede von MSER zurückgegebene Region können Sie eine konvexe Hülle und eine Polyapproximation anwenden, um eine solche zu erhalten:

Diese Art der Erkennung ist jedoch nützlich für die Live-Erkennung von mehr als einem einzelnen Bild, das nicht immer das beste Ergebnis liefert.

result

4
Flayn

Hier haben Sie @ Vanuans Code mit C++:

cv::cvtColor(mat, mat, CV_BGR2GRAY);
cv::GaussianBlur(mat, mat, cv::Size(3,3), 0);
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Point(9,9));
cv::Mat dilated;
cv::dilate(mat, dilated, kernel);

cv::Mat edges;
cv::Canny(dilated, edges, 84, 3);

std::vector<cv::Vec4i> lines;
lines.clear();
cv::HoughLinesP(edges, lines, 1, CV_PI/180, 25);
std::vector<cv::Vec4i>::iterator it = lines.begin();
for(; it!=lines.end(); ++it) {
    cv::Vec4i l = *it;
    cv::line(edges, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(255,0,0), 2, 8);
}
std::vector< std::vector<cv::Point> > contours;
cv::findContours(edges, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_TC89_KCOS);
std::vector< std::vector<cv::Point> > contoursCleaned;
for (int i=0; i < contours.size(); i++) {
    if (cv::arcLength(contours[i], false) > 100)
        contoursCleaned.Push_back(contours[i]);
}
std::vector<std::vector<cv::Point> > contoursArea;

for (int i=0; i < contoursCleaned.size(); i++) {
    if (cv::contourArea(contoursCleaned[i]) > 10000){
        contoursArea.Push_back(contoursCleaned[i]);
    }
}
std::vector<std::vector<cv::Point> > contoursDraw (contoursCleaned.size());
for (int i=0; i < contoursArea.size(); i++){
    cv::approxPolyDP(Mat(contoursArea[i]), contoursDraw[i], 40, true);
}
Mat drawing = Mat::zeros( mat.size(), CV_8UC3 );
cv::drawContours(drawing, contoursDraw, -1, cv::Scalar(0,255,0),1);
3
GBF_Gabriel

Verwenden Sie nach der Kantenerkennung die Hough-Transformation. Fügen Sie diese Punkte dann mit ihren Beschriftungen in eine SVM (unterstützende Vektormaschine) ein. Wenn die Beispiele glatte Linien aufweisen, hat SVM keine Schwierigkeiten, die erforderlichen Teile des Beispiels und andere Teile zu unterteilen. Mein Rat zu SVM, setzen Sie einen Parameter wie Konnektivität und Länge. Das heißt, wenn Punkte verbunden und lang sind, handelt es sich wahrscheinlich um eine Belegzeile. Dann können Sie alle anderen Punkte entfernen.

3
Hephaestus
  1. Verwenden Sie dann Konturen oder Hough auf einem der Cluster (intern)

  2. Use kmeans segment 2 cluster

  3. Then use contours or hough on one of the clusters (intenral)
1
user3452134