2017-12-02 2 views
1

InputImageopencv Entzerren eine Kontur

enter image description here

ResultImage

enter image description here

Ich habe die größte Kontur in dem Bild der Lage, zu filtern, das Token zu erkennen.

Ich habe Warp-Wahrnehmung angewendet, aber es schneidet nur das Bild an den Rändern der Kontur, sonst nichts.

Ich möchte, dass das erkannte Token ganz aus dem Rest des Bildes herausgeschnitten wird, und zwar unter Beibehaltung der Proportionen, so dass das Ergebnisbild aufrecht und gerade sein muss. Dann werde ich weitermachen und die Blobs im Token finden, um die darin markierten Daten zu erkennen.

 return warp(srcMat, topLeft, topRight, bottomLeft, bottomRight); 

mit diesem::

 return warp(srcMat, topLeft, topRight, bottomRight, bottomLeft); 

Ergebnis Update 1:

private Mat processMat(Mat srcMat) { 
    Mat processedMat = new Mat(); 
    Imgproc.cvtColor(srcMat, processedMat, Imgproc.COLOR_BGR2GRAY); 
    Imgproc.GaussianBlur(processedMat, processedMat, new Size(5, 5), 5); 
    Imgproc.threshold(processedMat, processedMat, 127, 255, Imgproc.THRESH_BINARY); 
    List<MatOfPoint> contours = new ArrayList<>(); 
    Mat hierarchy = new Mat(); 
    Imgproc.findContours(processedMat, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); 

    double maxVal = 0; 
    int maxValIdx = 0; 
    for (int contourIdx = 0; contourIdx < contours.size(); contourIdx++) { 
     double contourArea = Imgproc.contourArea(contours.get(contourIdx)); 
     if (maxVal < contourArea) { 
      maxVal = contourArea; 
      maxValIdx = contourIdx; 
     } 
    } 
    if (!contours.isEmpty()) { 
     Imgproc.drawContours(srcMat, contours, maxValIdx, new Scalar(0,255,0), 3); 
     Rect rect = Imgproc.boundingRect(contours.get(maxValIdx)); 
     Log.e("rect", "" + rect); 
     int top = srcMat.height(); 
     int left = srcMat.width(); 
     int right = 0; 
     int bottom = 0; 

     if(rect.x < left) { 
      left = rect.x; 
     } 
     if(rect.x+rect.width > right){ 
      right = rect.x+rect.width; 
     } 
     if(rect.y < top){ 
      top = rect.y; 
     } 
     if(rect.y+rect.height > bottom){ 
      bottom = rect.y+rect.height; 
     } 

     Point topLeft = new Point(left, top); 
     Point topRight = new Point(right, top); 
     Point bottomRight = new Point(right, bottom); 
     Point bottomLeft = new Point(left, bottom); 

     return warp(srcMat, topLeft, topRight, bottomLeft, bottomRight); 
    } 
    return null; 
} 

Mat warp(Mat inputMat, Point topLeft, Point topRight, Point bottomLeft, Point bottomRight) { 
    int resultWidth = (int)(topRight.x - topLeft.x); 
    int bottomWidth = (int)(bottomRight.x - bottomLeft.x); 
    if(bottomWidth > resultWidth) 
     resultWidth = bottomWidth; 

    int resultHeight = (int)(bottomLeft.y - topLeft.y); 
    int bottomHeight = (int)(bottomRight.y - topRight.y); 
    if (bottomHeight > resultHeight) { 
     resultHeight = bottomHeight; 
    } 

    Mat outputMat = new Mat(resultWidth, resultHeight, CvType.CV_8UC1); 

    List<Point> source = new ArrayList<>(); 
    source.add(topLeft); 
    source.add(topRight); 
    source.add(bottomLeft); 
    source.add(bottomRight); 
    Mat startM = Converters.vector_Point2f_to_Mat(source); 

    Point ocvPOut1 = new Point(0, 0); 
    Point ocvPOut2 = new Point(resultWidth, 0); 
    Point ocvPOut3 = new Point(0, resultHeight); 
    Point ocvPOut4 = new Point(resultWidth, resultHeight); 
    List<Point> dest = new ArrayList<>(); 
    dest.add(ocvPOut1); 
    dest.add(ocvPOut2); 
    dest.add(ocvPOut3); 
    dest.add(ocvPOut4); 
    Mat endM = Converters.vector_Point2f_to_Mat(dest); 

    Mat perspectiveTransform = Imgproc.getPerspectiveTransform(startM, endM); 

    Imgproc.warpPerspective(inputMat, outputMat, perspectiveTransform, new Size(resultWidth, resultHeight)); 
    return outputMat; 
} 

UPDATE 1

Dieser ersetzt

Result Now

UPDATE 2

public Mat warp(Mat inputMat, MatOfPoint selectedContour) { 
    MatOfPoint2f new_mat = new MatOfPoint2f(selectedContour.toArray()); 
    MatOfPoint2f approxCurve_temp = new MatOfPoint2f(); 
    int contourSize = (int) selectedContour.total(); 
    Imgproc.approxPolyDP(new_mat, approxCurve_temp, contourSize * 0.05, true); 

    double[] temp_double; 
    temp_double = approxCurve_temp.get(0,0); 
    Point p1 = new Point(temp_double[0], temp_double[1]); 
    temp_double = approxCurve_temp.get(1,0); 
    Point p2 = new Point(temp_double[0], temp_double[1]); 
    temp_double = approxCurve_temp.get(2,0); 
    Point p3 = new Point(temp_double[0], temp_double[1]); 
    temp_double = approxCurve_temp.get(3,0); 
    Point p4 = new Point(temp_double[0], temp_double[1]); 
    List<Point> source = new ArrayList<Point>(); 
    source.add(p1); 
    source.add(p2); 
    source.add(p3); 
    source.add(p4); 
    Mat startM = Converters.vector_Point2f_to_Mat(source); 

    int resultWidth = 846; 
    int resultHeight = 2048; 

    Mat outputMat = new Mat(resultWidth, resultHeight, CvType.CV_8UC4); 

    Point ocvPOut1 = new Point(0, 0); 
    Point ocvPOut2 = new Point(0, resultHeight); 
    Point ocvPOut3 = new Point(resultWidth, resultHeight); 
    Point ocvPOut4 = new Point(resultWidth, 0); 
    List<Point> dest = new ArrayList<Point>(); 
    dest.add(ocvPOut1); 
    dest.add(ocvPOut2); 
    dest.add(ocvPOut3); 
    dest.add(ocvPOut4); 
    Mat endM = Converters.vector_Point2f_to_Mat(dest); 

    Mat perspectiveTransform = Imgproc.getPerspectiveTransform(startM, endM); 

    Imgproc.warpPerspective(inputMat, outputMat, perspectiveTransform, new Size(resultWidth, resultHeight), 
      Imgproc.INTER_CUBIC); 
    return outputMat; 
} 

Ergebnis Update 2:

ich meine Verzerrungsfunktion ein wenig verändert haben und der Code angebracht ist. Das resultierende Bild wird jedoch irgendwie in die falsche Richtung gedreht. Kannst du mir sagen, welches der richtige Weg ist?

Die Ausrichtung des Android-Geräts ist auf: Hochformat eingestellt und das Eingabebild ist ebenfalls im Hochformat.

result_update_2

UPDATE 3

Ich habe es geschafft, wie so das Token zu begradigen, indem die Ecken Sortierung:

List<Point> source = new ArrayList<Point>(); 
    source.add(p2); 
    source.add(p3); 
    source.add(p4); 
    source.add(p1); 
    Mat startM = Converters.vector_Point2f_to_Mat(source); 

Ergebnis Update 3:

Result Update 3

Das resultierende Bild wird jedoch von der linken Seite beschnitten, ich habe keine Ahnung, wie ich das angehen soll. Ich habe es geschafft, das Eingabebild zu glätten, wenn der Token nach rechts oder links geneigt ist und das Ausgabebild trotzdem gerade ist. Wenn das Eingabebild das Token jedoch bereits zentriert und gerade ausgerichtet hat.es dreht sich das Token wie so, mit dem gleichen Code:

Ausgabe Update 3:

Issue Update 3

Antwort

1

Die Transformation um das Ticket zu entzerren ist in der Nähe einer affinen ein. Sie können es erhalten, indem Sie den Umriss mit einem Parallelogramm annähern. Sie finden die Eckpunkte des Parallelogramms als die am weitesten links, oben, rechts und unten liegenden Punkte.

Eigentlich brauchen Sie nur drei Vertices (und die vierte kann von diesen neu berechnet werden). Vielleicht ist eine Kleinste-Quadrate-Anpassung des Parallelogramms möglich, ich weiß es nicht.

Eine weitere Option ist die Betrachtung einer homographischen Transformation, die aus vier Punkten definiert wird (aber die Berechnung ist viel komplexer). Es wird die Perspektive berücksichtigen. (Sie könnten hier einen Einblick bekommen: https://www.codeproject.com/Articles/674433/Perspective-Projection-of-a-Rectangle-Homography.)

Um das Bild aufzurichten, genügt es, die inverse Transformation anzuwenden und ein Rechteck abzurufen. Wie auch immer, Sie werden feststellen, dass die Größe dieses Rechtecks ​​unbekannt ist, so dass Sie es beliebig skalieren können. Am schwierigsten ist es, ein geeignetes Seitenverhältnis zu finden.

+0

Ich dachte 'WarpPerception' wurde für das verwendet, was ich hier erreichen möchte. Ich habe eine Kontur, die den Token-Bereich umhüllt, den ich entzerren möchte. Kann ich die Ecken nicht von der Kontur bekommen, die ich gefiltert habe? kannst du dir bitte den Code, den ich gepostet habe, ansehen und teilen, was falsch ist, da das Hauptbild nur auf den Konturecken zugeschnitten und nicht gerade gerendert wird. –

+0

@MohsinFalak "Bekomme ich nicht die Ecken von der Kontur, die ich gefiltert habe?": Hast du wirklich meinen Beitrag gelesen? –

+0

Ich entschuldige mich dafür, ein Amateur zu sein, aber ich bin frisch mit OpenCV und Ihre Hilfe wird sehnsüchtig Schritt für Schritt erwartet. Ich habe mit diesem Thema Fortschritte gemacht und die Frage wird aktualisiert. Kannst du es mir ansehen und mir bei diesem Problem helfen? –

Verwandte Themen