C# Bild automatisch erkennen und ausschneiden

-Rayz-

Lieutenant
Registriert
Okt. 2010
Beiträge
897
Hallo erstmal,

ich weiß ja nicht ob sie es schon wussten... aber ich habe da mal wieder ein Problemchen.
Ich beschäftige mich gerade mit der Gesichtserkennung. Hier gibt es ja schon ordentliche Open Source Programme welche man nutzen kann. Das funktioniert auch ganz gut.

Hier die Ergebnisse:

Hier das Eingescannte:
Scan.jpg

Und hier die Gesichtserkennung:

detectedFace.jpg

Nun zu dem Problem. Bei dem Original Bild möchte ich mir nur die ID Card anzeigen lassen. Sprich das Schwarz muss weg. Da das Bild immer eine andere Größe haben kann, kann ich nicht einfach einen festen Bereich definieren und diesen dann ausschneiden. Ich müsste den Kartenrahmen erkennen und diesen ausschneiden oder aber sagen, dass dunkle Pixel außerhalb der ID Card weggeschnitten werden allerdings gibt es ja noch den tollen, hellen Staub....
Freund Google habe ich schon bemüht nur sind meine Suchbegriffe wohl falsch. Ich weiß auch gerade nicht wirklich, nach was ich hier genau suchen soll.
Wie könnte man das Problem angehen?

Vielen Dank
 
Danke,

okay den Rahmen habe ich. Nur noch ausschneiden.. irgendwie :freak:

test2.jpg
Ergänzung ()

Also ich habe hier die Koordinaten und aus denen bastel ich mir ein Rectangle.
Dann hab ich versucht, mit DrawImage den Bereich auszuschneiden..



klappt nur -noch- nicht -_-
kann es sein, dass der Rahmen gar nicht stimmt welcher ermittelt wird..
Ergänzung ()

So, der Rahmen war auf max. 400*400 eingestellt. Das habe ich mal geändert.

So ganz klappt es aber mit DrawImage noch nicht.

Hier mal drei Bilder was so passiert:
Das Ausgangsbild:
Scan.jpg

Hier wird der Rahmen erkannt
test.jpg

Und hier der Ausschnitt.
test2.jpg

Passt ja nicht so wirklich...



Nun muss ich nur noch überlegen, wieso dieser Prozess so lange dauert (5-6 Sekunden)
 
Zuletzt bearbeitet:
Wo ich gerade über einen anderen User hier gemotzt habe:

Möchte dich sehr dafür loben, nicht einfach "Problem gelöst, bitte Thread schließen" gepostet zu haben, sondern deine Schritte zur Lösung festzuhalten. Das ist wirklich vorbildlich.

Lass dich knuddeln :3
 
Danke :)

Versuche generell auch die Lösung zu posten. Wenn man einen Thread aufmacht um Hilfe zu bekommen, kann man wenigstens die Lösung auch in den Thread schreiben damit evtl. anderen geholfen wird.

So, leider ist das Thema noch nicht abgeschlossen.
Hier mal wieder ein Bild:

detect.JPG

Ich schaffe es nur selten, dass dieser Ausweis als Rectangle erkannt wird. Ich habe an den Parametern rumgetestet aber leider habe ich es damit nicht geschafft, dass er in diesem Beispiel das Rechteck erkennt.


Verstehe auch nicht wieso ich es nicht schaffe, dass er das für mich eindeutige Rechteck erkennt.
Ergänzung ()

Hier noch ein Link zu dem Code. Evtl. besser lesbar.

http://www.emgu.com/wiki/index.php/Shape_%28Triangle,_Rectangle,_Circle,_Line%29_Detection_in_CSharp
Ergänzung ()

Glaube es liegt an der Farbe. Auf der Rückseite der Karte sind 6 Rechtecke zu finden. diese sind aber im Gegensatz zur Karte selbst alle schwarz und wurden auch sofort und problemlos erkannt. Ich muss das Bild also vorher farblich irgendwie abändern.

Wenn ich den Pass scanne, dann ist dieser ja hell und der Rest dunkel. Dieses müsste ich einfach nur umdrehen. Sprich das helle wird dunkel und der dunkle Teil wird hell.

Oder hat wer eine andere Idee bzw. evtl. einen Tipp für die Umsetzung meiner Idee?
Ergänzung ()

So langsam bin ich ja etwas verwirrt.

Folgenden Code habe ich mal im Bild abgespeichert:

Code:
            Image<Gray, Byte> cannyEdges = gray.Canny(cannyThreshold, cannyThresholdLinking);

            cannyEdges.Save("canny.jpg");

Ergebnis:

canny.jpg

Hier sieht man doch alles!!! Wieso wird dann das Rechtkeck nicht erkannt. An der Krümmen der "Ecken" kann es doch nicht liegen.
Ergänzung ()

Und weiter gehts.
Da der Scan des Bildes einfach zu schlecht ist, habe ich versucht durch Gamma/Smooth/Sharping das Ausgangsbild zu verbessern. Auch habe ich alle Buchstaben entfernt und am Ende ein schwarz/weiß Bild mit otsu (Threshold) erstellt.

Ich habe jedes einzelne Bild nach jedem Schritt getestet und nur bei meiner Erfolg gehabt.

Hier nun das Bild:

detect.JPG

Ich habe die Schärfe erhöht.

Code:
            Emgu.CV.CvInvoke.cvSmooth(img, c_img, Emgu.CV.CvEnum.SMOOTH_TYPE.CV_GAUSSIAN, 5, 5, 9, 9);
            Emgu.CV.CvInvoke.cvAddWeighted(img, 1.5, c_img, -0.5, 0, img);

Nun wird ein Rechteck erkannt. Mal sehen, ob das auch so bleibt...
 
Zuletzt bearbeitet:
AW: Bild automatisch erkennen und ausschneiden mit OpenCv

LEider kriege ich es nicht schnell UND zuverlässig zum laufen. Es muss doch möglich sein, dass das Objekt erkannt wird. Ich kann mit den Werten rumspielen wie ich will aber es ist einfach jedesmal anders. Dennoch versteh ich nicht wieso die Karte so schlecht erkannt wird.
Bild seht ihr ja oben um welches es geht.
Habe nun alles in eine Schleife gepackt die solange durchlaufen wird, bis das Bild erkannt werden kann. Bei jedem Durchlauf der Schleife ändere ich auch einige Werte damit die Bildbearbeiten auch sich jedesmal ändert. Feste Werte bringen hier nichts... es klappt ständig mit anderen Werten:

Was kann ich noch machen?
Sollte ich evtl. etwas was ganzeres probieren?
Oft reicht ja 1-3 durchläufe aber oft auch über 10!
Generell soll aber das Bild natürlich bei dem ersten Durchlauf schon erkannt werden.
 
Zuletzt bearbeitet:
1. Wenn du ein bisschen rumlesen willst google mal nach den drei Wörtern: opencv find rectangle
Da sind viele Lösungen vorgestellt die zuverlässig funktionieren und sich durch Standardverfahren unkompliziert ausprobieren lassen wenn man eh eine BV Bibliothek zur Verfügung hat.

2. Alternativer einfacher Vorschlag: Mach ne HoughTransformation für Linien auf einem Kanten-Bild (Canny, Sobel...) und suche 4 relative Maxima die zueinander jeweils pi/2 also 90° versetzt sind.
Wenn du deinen Hough-Raum wie hier aufbaust, fasst du zB einmal alle Spalten zu je einem Pixel zusammen und suchst dann in diesem 1D Array (Länge = Hough-Raum-Breite) mit einer sinnvollen Fenstergröße (Toleranz im Winkel) das Maximum für diese 4 Stellen. Anschließend suchst du die genaue Position dieser 4 Maxima im original HoughRaum (Die 4 Spalten stehen nun fest und du musst nur die exakte Position als Maximum der jeweiligen Spalte ablesen).
Theoretisches Problem im zweiten Schritt, was sich aber auch lösen lässt: Es gibt im Bild evtl eine stärkere Kante die parallel zu einer der gesuchten Rechteck-Kanten liegt. Somit würde ein nicht-geschlossenes Rechteck erkannt werden.
Voraussetzung: Du guckst quasi senkrecht auf das Bild damit die Kanten quasi 90° zueinander liegen. Sollte der Ausweis irgendwo in einem Foto auf dem Tisch liegen muss die Fenstergröße bei der Suche im 1D Array groß genug gewählt werden um auch ein 4Eck zuzulassen. (Scherung? oder wie hieß das)

3. Um mal ein ganz anderes Verfahren vorzuschlagen poste ich mal einen Screenshot aus meinem eigenen aktuellen Bildverarbeitungsprojekt. Das Projekt wird leider nicht open source aber die Idee liefert dir evtl auch ein Screenshot: pos_example.png
Das ganze dauert nichtmal eine ms für quasi jede beliebige Auflösung.
 
Zuletzt bearbeitet:
So wie kuddlmuddl´s 3. wäre auch mein Ansatz.

Versuche nicht das Rechteck im ganzen Bild zu finden, sondern schau das Du eine horizontale und eine vertikale Kante in einem begrenzten Bereich suchst. Mach Dir also Suchfenster wie in kuddlmuddl´s Bild angedeutet.

Aus den Schnittpunten kannst Du dann das Rechteck berechnen.

Wie einfach sich das mit opencv bewerkstelligen lässt kann ich leider nicht sagen. Meine Erfahrung mit Bildverarbeitung bezieht sich da eher auf fertige Toolsets im industriellen Bereich, da sind die Funktionen schon weiter ausprogrammiert und man muss nicht alles zu Fuß machen ;)
 
Also zu Punkt 2 und 3 fehlt mir einfach die Zeit um das mal eben umzusetzen. Gerade denn wenn ich von Bildbearbeitung und Vektoren keine Ahnung habe.

Zu Punkt 1 suche ich schon seit Ewigkeiten das richtige. Ich weiß aber nicht, was mich von diesen ganzen Beispielen weiterbringen soll. Mein jetziges Programm war ja auch nur ein Beispiel.


Unter Opencv shape detection c# oder OpenCv rectankle/object wie auch immer detection/tracking/finding etc. c# finde ich einfach nichts.
Edge Detection Countor detection diese ganzen Beispielprogramme funktionieren bei meinem Beispielbild nicht.
Ergänzung ()

Auch habe ich das SURF Feature getestet aber das bringt mich ja nur weiter, wenn ich ein Bild zum vergleichen habe und das habe ich nicht. Genauso wie bei TemplateMatching
 
-Rayz- schrieb:
Also zu Punkt 2 und 3 fehlt mir einfach die Zeit um das mal eben umzusetzen. Gerade denn wenn ich von Bildbearbeitung und Vektoren keine Ahnung habe.

Bildverarbeitung ist halt ein komplexes Thema und wenn dann die Ursprungsbilder schlecht oder extrem unterschiedlich sind steigt der Aufwand nochmal.

Hast Du schon versucht das Bild vorzuverarbeiten?

Hier hab ich mal einfach bei Grauwert 130 Binarisiert und dann eine Open Morphologie Operation angewendet um die kleinen Elemente verschwinden zu lassen. Das sollte mit opencv auch möglich sein. Wenn die Grauwertschwelle dann noch dynamisch an das Bild angepasst wird sollte damit ein brauchbares Ergebniss erzielt werden können.

Scan_bin.jpg scan_open.jpg
 
So, das habe ich mal umgesetzt aber diesen Fall kann ich damit gerade nicht lösen...

Ausgangsbild:

Scan.jpg

Mit diesem Code wird dann das daraus:

Code:
                            CvInvoke.cvThreshold(img.Convert<Gray, byte>(), gray, 130, 255, Emgu.CV.CvEnum.THRESH.CV_THRESH_OTSU);
                            Image<Gray, Byte> bgray = gray.Erode(4);

Scan_bgray.jpg

Sieht schon ganz ordentlich aus aber die Fehler im Bild verhindern ein erfolgreiches erkennen als Rectangle...
Ich muss doch irgendwie die Schwellenwerte noch so ändern können, dass das nun als Rechteck erkannt wird.

Hier habe ich aber bereits vieles ausprobiert.
Code:
                                LineSegment2D[] lines = cannyEdges.HoughLines
                                    (
                                        cannyThreshold,
                                        cannyThresholdLinking,
                                        1,
                                        Math.PI / 180,
                                        Convert.ToInt16(gray.Width * 0.3),
                                        gray.Width * 0.4,
                                        gray.Width * 0.1
                                    )[0];

Und damit auch:

Code:
                                        Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.05, storage);
Ergänzung ()

Allerdings kann ich wenn das Bild nun so aussieht überhaupt kein Objekt mehr erkennen. Hab mit einem opencv tool das Bild anschließend mit allen möglichen Einstellungen getestet aber ich konnte aus dem schwarz/weiß Bild kein Objekt rausbekommen...
Verkleinern kann ich das Bild seltsamerweise vorher auch nicht weil dann die cvThreshold Funktion nicht mehr funktioniert...
 
So funktioniert die Sache schon recht ordentlich, perfekt leider nicht aber ist ok.

Code:
double cannyThreshold = 120.0;
            double cannyThresholdLinking = 80;

            
        }

Ich habe aber schon wieder ein Problem mit dem erstellen Bild!!! Es kommt der Fehler:

allgemeiner Fehler in GDI

ok dispose vergessen...
 
Zuletzt bearbeitet:
Zurück
Oben