2012-11-23 54 views
7

enter image description here我有許多凹槽表面的圖片。在大多數情況下,切槽的邊緣形成平行線,因此Canny和Hough變換非常適合檢測線條並進行一些特徵分析。然而,在幾個地方開槽已經損壞,邊緣不再平行。OpenCV開槽檢測

我正在尋找一種簡單的方法來檢查某個邊緣是否是直線,或者是否存在任何直線上的間隙或偏差。我正在考慮像線性插值中的R平方參數,但在這裏我需要一個更依賴於位置的參數。你有任何其他思想如何描述邊緣?

我附上canny邊緣檢測後開槽的圖片。這裏邊緣是直線,開槽很好。不幸的是,我目前無法訪問損壞開槽的圖片。但是,在切槽受損的照片中,線條會有較大的間隙(至少爲圖片尺寸的10%)或不平行。

+0

也許你可以包含一個示例圖像? –

+0

歡迎來到Stackoverflow。請仔細查看我的答案,然後如果它對您有幫助,請對其進行投票。您可以點擊附近的複選框將其選爲您問題的正式答案。通過這樣做,你將通過保持這個線程的組織來幫助像你和我們這樣的未來訪問者。 – karlphillip

回答

6

該技術的核心我在下面分享使用cv::HoughLinesP()查找灰度圖像中的線段。

應用程序首先將輸入圖像加載爲灰度。然後,它執行一個基本預處理操作來增強圖像的某些特性,旨在改善由cv::HoughLinesP()執行檢測:

#include <cv.h> 
#include <highgui.h> 

#include <algorithm> 

// Custom sort method adapted from: http://stackoverflow.com/a/328959/176769 
// This is used later by std::sort() 
struct sort_by_y_coord 
{ 
    bool operator()(cv::Vec4i const& a, cv::Vec4i const& b) const 
    { 
     if (a[1] < b[1]) return true; 

     if (a[1] > b[1]) return false; 

     return false; 
    } 
}; 


int main() 
{ 
    /* Load input image as grayscale */ 

    cv::Mat src = cv::imread("13531682.jpg", 0); 

    /* Pre-process the image to enhance the characteristics we are interested at */ 

    medianBlur(src, src, 5); 

    int erosion_size = 2; 
    cv::Mat element = cv::getStructuringElement(cv::MORPH_CROSS, 
             cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1), 
             cv::Point(erosion_size, erosion_size)); 
    cv::erode(src, src, element); 
    cv::dilate(src, src, element); 

    /* Identify all the lines in the image */ 

    cv::Size size = src.size(); 
    std::vector<cv::Vec4i> total_lines; 
    cv::HoughLinesP(src, total_lines, 1, CV_PI/180, 100, size.width/2.f, 20); 

    int n_lines = total_lines.size(); 
    std::cout << "* Total lines: "<< n_lines << std::endl; 

    cv::Mat disp_lines(size, CV_8UC1, cv::Scalar(0, 0, 0)); 

    // For debugging purposes, the block below writes all the lines into disp_lines 
    // for (unsigned i = 0; i < n_lines; ++i) 
    // { 
    //  cv::line(disp_lines, 
    //    cv::Point(total_lines[i][0], total_lines[i][2]), 
    //    cv::Point(total_lines[i][3], total_lines[i][4]), 
    //    cv::Scalar(255, 0 ,0)); 
    // } 
    // cv::imwrite("total_lines.png", disp_lines); 

此時,所有檢測到可以寫入用於可視化文件中的線段用途:

在這一點上,我們需要重新梳理我們的行向量,因爲cv::HoughLinesP()沒有做到這一點,我們需要的載體排序以能夠識別線路組,通過測量和比較線路之間的距離:

/* Sort lines according to their Y coordinate. 
     The line closest to Y == 0 is at the first position of the vector. 
    */ 

    sort(total_lines.begin(), total_lines.end(), sort_by_y_coord()); 

    /* Separate them according to their (visible) groups */ 

    // Figure out the number of groups by distance between lines 
    std::vector<int> idx_of_groups; // stores the index position where a new group starts 
    idx_of_groups.push_back(0); // the first line indicates the start of the first group 

    // The loop jumps over the first line, since it was already added as a group 
    int y_dist = 35; // the next groups are identified by a minimum of 35 pixels of distance 
    for (unsigned i = 1; i < n_lines; i++) 
    { 
     if ((total_lines[i][5] - total_lines[i-1][6]) >= y_dist) 
     { 
      // current index marks the position of a new group 
      idx_of_groups.push_back(i); 
      std::cout << "* New group located at line #"<< i << std::endl;   
     } 
    } 

    int n_groups = idx_of_groups.size(); 
    std::cout << "* Total groups identified: "<< n_groups << std::endl; 

上面簡單地存儲在新vector<int>線的向量的索引位置的代碼的最後部分,所以我們知道哪些線開始一個新組。

例如,假設存儲在新矢量中的索引是:0 4 8 12。記住:他們定義每個組的開始。這意味着這些組的結束行是:0, 4-1, 4, 8-1, 8, 12-1, 12

知道,我們寫出下面的代碼:

/* Mark the beginning and end of each group */ 

    for (unsigned i = 0; i < n_groups; i++) 
    { 
     // To do this, we discard the X coordinates of the 2 points from the line, 
     // so we can draw a line from X=0 to X=size.width 

     // beginning 
     cv::line(disp_lines, 
       cv::Point(0, total_lines[ idx_of_groups[i] ][7]), 
       cv::Point(size.width, total_lines[ idx_of_groups[i] ][8]), 
       cv::Scalar(255, 0 ,0)); 

     // end  
     if (i != n_groups-1) 
     { 
      cv::line(disp_lines, 
        cv::Point(0, total_lines[ idx_of_groups[i+1]-1 ][9]), 
        cv::Point(size.width, total_lines[ idx_of_groups[i+1]-1 ][10]), 
        cv::Scalar(255, 0 ,0)); 
     } 
    } 
    // mark the end position of the last group (not done by the loop above)  
    cv::line(disp_lines, 
      cv::Point(0, total_lines[n_lines-1][11]), 
      cv::Point(size.width, total_lines[n_lines-1][12]), 
      cv::Scalar(255, 0 ,0)); 

    /* Save the output image and display it on the screen */ 

    cv::imwrite("groups.png", disp_lines); 

    cv::imshow("groove", disp_lines); 
    cv::waitKey(0); 
    cv::destroyWindow("groove"); 

    return 0; 
} 

而產生的圖像是:

這不是一個完美的比賽,但已經很接近了。稍微調整一下,這種方法可以變得更好。我首先要編寫一個更智能的邏輯sort_by_y_coord,它應該放棄X座標(即小線段)之間距離較短的線條,以及X軸線上沒有完全對齊的線條(如第二組中的線條在輸出圖像中)。在花時間評估應用程序生成的第一個圖像之後,這個建議更有意義。

祝你好運。

+1

謝謝!優秀的回覆。 – marc

4

立即想到的是Hough Transform。這是一個線條空間的投票方案,它採用每條可能的線條並給你一個分數。在上面鏈接的代碼中,您可以簡單地設置一個閾值,該閾值接近螺紋槽/線的〜10%。

+1

如果線條不是很平行,或者它們是彎曲的,那麼Marc可以在圖像的NxN區域中使用Hough來查找分段直線。然後可以將短線段連接起來以找到曲線。 – Rethunk