2015-04-24 6 views
6

Ich versuche, mehr als ein Quadrat (Marker) in meinem Bild zu erfassen, wie ich hier fragen Detect Marker Position in 2D imageOpenCV - C++ zu Java - Template Match

Es gibt einen Mann, der mir in C eine Lösung zeigte ++, ist es hier :

#include <iostream> 

#include <opencv2/highgui/highgui.hpp> 
#include <opencv2/imgproc/imgproc.hpp> 


//See: http://docs.opencv.org/doc/tutorials/imgproc/histograms/template_matching/template_matching.html 
//See: http://answers.opencv.org/question/60382/detect-markers-position-in-2d-images/ 
int main() { 
    cv::Mat img, templateImg, result; 
    cv::VideoCapture capture("http://answers.opencv.org/upfiles/14297307634571599.png"); 
    if(capture.isOpened()) { 
    capture >> img; 
    } else { 
    return -1; 
    } 

    capture = cv::VideoCapture("http://answers.opencv.org/upfiles/14297308125543022.png"); 
    if(capture.isOpened()) { 
    capture >> templateImg; 
    } else { 
    return -1; 
    } 

    /// Reduce the size of the image to display it on my screen 
    cv::resize(img, img, cv::Size(), 0.5, 0.5); 
    /// Reduce the size of the template image 
    /// (first to fit the size used to create the image test, second to fit the size of the reduced image) 
    cv::resize(templateImg, templateImg, cv::Size(), 0.25, 0.25); 

    cv::Mat img_display; 
    img.copyTo(img_display); 

    // Create the result matrix 
    int result_cols = img.cols - templateImg.cols + 1; 
    int result_rows = img.rows - templateImg.rows + 1; 

    result.create(result_rows, result_cols, CV_32FC1); 

    /// Do the Matching and Normalize 
    cv::matchTemplate(img, templateImg, result, CV_TM_CCORR_NORMED); 
    cv::normalize(result, result, 0, 1, cv::NORM_MINMAX, -1, cv::Mat()); 

    /// Localizing the best match with minMaxLoc 
    double minVal; double maxVal; cv::Point minLoc; cv::Point maxLoc; 
    cv::Point matchLoc; 

    for(;;) { 
    cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat()); 
    matchLoc = maxLoc; 
    std::cout << "Max correlation=" << maxVal << std::endl; 
    if(maxVal < 0.8) { 
     break; 
    } 

    /// Show me what you got 
    cv::rectangle(img_display, matchLoc, cv::Point(matchLoc.x + templateImg.cols , matchLoc.y + templateImg.rows), 
     cv::Scalar::all(0), 2, 8, 0); 
    cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2), 
     cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2), cv::Scalar::all(0), 2, 8, 0); 

    cv::imshow("result", result); 
    cv::waitKey(0); 

    /// Fill the detected location with a rectangle of zero 
    cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2), 
     cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2), cv::Scalar::all(0), -1); 
    } while (maxVal > 0.9); 


    cv::imshow("result", result); 
    cv::imshow("img_display", img_display); 
    cv::waitKey(0); 

    return 0; 
} 

Die for-Schleife ist verantwortlich mehr als ein Marker zu finden und erkennen, ist es, ich versuche, es zu meinem Java-Code anzupassen, und ich bin immer eine Endlosschleife hier ist mein Code:

public void run(String inFile, String templateFile, String outFile, int match_method) { 
     System.out.println("\nRunning Template Matching"); 


    Mat img = Highgui.imread(inFile); 
    Mat templ = Highgui.imread(templateFile); 

    ///Create the result matrix 
    int result_cols = img.cols() - templ.cols() + 1; 
    int result_rows = img.rows() - templ.rows() + 1; 
    Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1); 

    ///Do the Matching and Normalize 
    Imgproc.matchTemplate(img, templ, result, match_method); 
    Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat()); 

    Point matchLoc; 
    Point maxLoc; 
    Point minLoc; 

    MinMaxLocResult mmr; 

    boolean iterate = true; 
    while(iterate){ 

    ///Localizing the best match with minMaxLoc 
    mmr = Core.minMaxLoc(result); 
    matchLoc = mmr.maxLoc; 


    if(mmr.maxVal < 0.8) 
    { 
     iterate = false; 
    } 



    ///Show me what you got 
    Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), 
      matchLoc.y + templ.rows()), new Scalar(0, 255, 0)); 

    } 

    // Save the visualized detection. 
    System.out.println("Writing "+ outFile); 
    Highgui.imwrite(outFile, img); 

} 

I n otice, dass die Funktion minMaxLoc mehr Argumente in C++ als in Java hat, vielleicht ist das das Problem? Warum kann ich in Java nicht dasselbe Verhalten erreichen? Kann mir jemand helfen?

Vielen Dank im Voraus

Antwort

3

Wie cyriel sagte, Sie haben vergessen, Nullen für die maximale Position zu füllen und damit haben Sie die Endlosschleife.Möchte er Ihnen zu erklären, vergessen, dass

for each iteration 
    find the max location 
    check if max value is greater than desired threshold 
    if true 
    show me what is max 
    else 
    break // not found anything that matches 
    make the existing max to be zero and continue to search for other max// you forgot this and hence infinite loop 
    end 

auf das Problem Weitere Einsicht, Sie die folgenden Zeilen in C++ Code kommentieren und führen Sie es, Sie ähnliches Problem auftreten. (Hier cv :: Scalar :: alle (0), - 1 args sagt füllen die bestehende max/gefunden Region mit 0 und weiter)

// Fill the detected location with a rectangle of zero 
    cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols/2, matchLoc.y - templateImg.rows/2), 
     cv::Point(matchLoc.x + templateImg.cols/2, matchLoc.y + templateImg.rows/2), cv::Scalar::all(0), -1); 

Hoffe, es hilft.

+0

Es half sehr, ich änderte den Code, den ich in Java hatte und jetzt funktioniert, hier ist es: http://pastebin.com/HXP9JaMN Vielen Dank! ;) – TiagoM

+0

Ich merke, dass die Vorlage nicht funktioniert, um meine schwarzen Quadrate zu erkennen, ich muss mit @RobAu Lösung gehen ... – TiagoM

+1

Das ist in Ordnung. Ihre Frage scheint eher das Problem der Endlosschleife mit dem Titel Opencv C++ zu Java - Template match zu lösen. Sie können hier im stackoverflow nach weiteren Fragen suchen, die mit der Übereinstimmung von Vorlagen oder dem Suchen von Quadraten oder Objekten in Bildern zusammenhängen. – sriram

1

Sie haben vergessen, zweites Rechteck zu zeichnen. Originalcode:

cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2), 
    cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2), cv::Scalar::all(0), 2, 8, 0); 

... 

/// Fill the detected location with a rectangle of zero 
cv::rectangle(result, cv::Point(matchLoc.x - templateImg.cols/2 , matchLoc.y - templateImg.rows/2), 
    cv::Point(matchLoc.x + templateImg.cols/2 , matchLoc.y + templateImg.rows/2), cv::Scalar::all(0), -1); 

jedoch in Ihrem Code gibt es nur so viel:

Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), 
     matchLoc.y + templ.rows()), new Scalar(0, 255, 0)); 

Den zweiten Teil des Original-Code am wahrscheinlichsten ist entscheidend - es zieht gefüllt, nicht nur eine Form wie erste Zeile.

Btw diese Linie Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0)); ist wahrscheinlich auch falsch - 1) Sie sollten Rechteck auf result Matte, nicht auf img Matte ziehen. 2) Im Originalcode gibt es Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0)); und in Ihrem Code new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()). Sind Sie sicher, dass es in Ordnung ist?

+0

Nein, das Problem ist nicht das zweite Rechteck, eigentlich brauche ich es nicht. Das Problem ist auf der while (Iteration), die nicht endet, ich bekomme eine Endlosschleife, denke ich. Ich habe versucht, die Schleife aus dem C++ - Code zu kopieren, aber vielleicht ist noch etwas übrig, weißt du was? – TiagoM

1

Obwohl es helfen könnte, brauchen Sie nicht OpenCV/JavaCV für diese relativ einfache Aufgabe.

Im Grunde wollen Sie Regionen in ihrem Bild zu finden, das ist:

a: all black 
b: square 
c: a percentage of size of the total image 

Zuerst Schwelle des Bild so alle Pixel entweder schwarz oder weiß.

Mein Ansatz wäre, die verbundenen Komponenten zu finden, indem Sie das Bild Pixel für Pixel abtasten, bis Sie ein schwarzes Pixel erreichen. Dann füllen Sie "flood fill" bis Sie alle schwarzen Pixel gesammelt haben. Markieren Sie sie (färben Sie sie anders) und speichern Sie sie in irgendeiner Struktur (Liste der Punkte) und wiederholen Sie, bis Sie das letzte Pixel des Bildes erreichten.

Für jede Komponente:

  • bestimmen, um den Begrenzungsrahmen (min und max x und y-Werte).
  • Berechnen Sie die Größe der Box und bestimmen, ob es klein/groß genug ist
  • Berechnen Sie den Prozentsatz der übereinstimmenden Pixel in diesem Feld. Wenn es nahe bei 100% ist, haben Sie eine quadratische Form.

Sie können die übereinstimmenden Begrenzungsrahmen wieder in Ihr Originalbild einfügen.

+0

Ich merke, dass die Vorlage nicht meine schwarzen Quadrate erkennen kann, ich muss diese Lösung verwenden, aber ich habe es nicht sehr gut verstanden .... Sie meinen, jedes Pixel des Bildes zu überprüfen? Wird das nicht viel Zeit brauchen? Kannst du mir einen Code geben? vielen Dank. Ich habe bereits die Bildschwelle, alle Pixel sind schwarz oder weiß – TiagoM

+0

Ich werde nicht Ihre Lösung codieren, verwenden Sie Google, um die Algorithmen zu finden. Ich habe mal so etwas auf einem Pentium III geschrieben und konnte trotzdem 30fps machen. Es hängt hauptsächlich von der Auflösung des Bildes ab. – RobAu

+0

Okay, erklär mir nur eines: Wenn ich mit dem ersten schwarzen Pixel fülle, füllt sich die Füllung nur mit den verbundenen schwarzen Pixeln, oder? gleiche Komponente? so kann ich alle schwarzen Komponenten auf dem Bild in eine Liste teilen? – TiagoM