2015-04-24 138 views
6

我想檢測我的形象不是方形(標記)更多,因爲我在這裏問Detect Marker Position in 2D image的OpenCV - C++到Java - 模板匹配

有一個傢伙,給我看C++的解決方案,這是:

#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; 
} 

for循環負責找到一個以上的標記,並檢測它,我想它適應我的Java代碼,我在這裏得到一個無限循環是我的代碼:

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); 

} 

我n注意函數minMaxLoc在C++中比在java中有更多的參數,也許這就是問題所在?爲什麼我不能在java中獲得相同的行爲,有人可以幫助我嗎?

非常感謝你提前

回答

3

正如cyriel所說,您忘記填寫最大位置的零,因此您得到了無限循環。可能是他忘了解釋你的是,

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 

要了解更多的問題, 你可以發表評論的C++代碼以下行並運行它,你會遇到類似的問題。 (這裏CV ::標量::所有(0), - 1個 ARGS說填寫現有的最大/發現區域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); 

希望它能幫助。

+0

它幫助了很多,我更改了我在java中的代碼,現在正在工作,這裏是:http://pastebin.com/HXP9JaMN 非常感謝! ;) – TiagoM

+0

我注意到模板匹配無法檢測到我的黑色方塊,我將需要使用@RobAu解決方案... – TiagoM

+1

這很好。你的問題似乎更多地解決標題爲Opencv C++到Java的無限循環問題 - 模板匹配。你可以在這裏搜索stackoverflow,以獲得更多與模板匹配相關的問題或查找圖像中的正方形或對象。 – sriram

1

你忘了畫第二個矩形。原代碼:

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); 

但是在你的代碼只有這個:

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

的原代碼,最有可能的第二部分是至關重要的 - 它吸引繚繞,不就像第一線的形狀。

Btw此行Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0));可能是錯誤的 - 1)您應該在result墊上繪製矩形,而不是在img墊上繪製矩形。 2)在原始代碼中有Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows()), new Scalar(0, 255, 0));和您的代碼new Point(matchLoc.x + templ.cols(), matchLoc.y + templ.rows())。你確定它沒事嗎?

+0

沒問題不是第二個矩形,實際上我不需要它。問題在於while(iteration)不會結束,我猜想我正在陷入無限循環。我嘗試從C++代碼複製循環,但可能還剩下一些東西,你知道嗎? – TiagoM

1

雖然它可能有幫助,但您不需要OpenCV/JavaCV來完成這項相對簡單的任務。

基本上,你想找到你的圖像是區域:

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

首先,門檻的圖像,因此所有像素都爲黑色或白色。

我的方法是通過逐像素掃描圖像找到連接的組件,直到您到達黑色像素。然後「洪水填充」,直到你有所有的黑色像素收集。標記它們(以不同顏色着色)並將它們存儲在某種結構(點列表)中並重復,直到到達圖像的最後一個像素。

對於每個組件:

  • 確定邊界框(最小值和最大值的x和y值)。
  • 計算 框的大小並確定它是否足夠小/大
  • 計算該框中匹配像素的百分比。如果它是 接近100%,則表示方形。

您可以將匹配的邊界框繪製回原始圖像。

+0

我注意到模板匹配無法檢測到我的黑色方塊,我需要使用這個解決方案,但是我沒有很好地理解它....您的意思是檢查圖像的每個像素?這不會花費太多時間嗎?你能給我一些代碼嗎?非常感謝你。我已經有了圖像閾值,所有的像素都是黑色或白色 – TiagoM

+0

我不打算編碼您的解決方案,使用谷歌找到算法。我曾經在奔騰III上寫過類似的東西,但仍然能夠做到30fps。它主要取決於圖像的分辨率。 – RobAu

+0

好的,請解釋我一件事:當我開始對第一個黑色像素進行填充填充時,填充只會對連接的黑色像素有效?相同的組件?所以我可以將圖像上的所有黑色組件分成一個列表? – TiagoM