2012-11-23 39 views
7

enter image description here Tôi có hình ảnh của một bề mặt có nhiều rãnh. Trong hầu hết các trường hợp, các cạnh của các rãnh song song tạo thành các đường thẳng nên việc chuyển đổi Canny và Hough hoạt động rất tốt để phát hiện các dòng và thực hiện một số đặc tính. Tuy nhiên, tại một số nơi rãnh được demaged và các cạnh không song song nữa.Phát hiện rãnh mở OpenCV

Tôi đang tìm kiếm một cách dễ dàng để kiểm tra xem một cạnh nhất định có phải là đường thẳng hay không hoặc có bất kỳ khoảng trống hoặc độ lệch nào từ đường thẳng hay không. Tôi đang nghĩ về một cái gì đó giống như tham số vuông R trong nội suy tuyến tính, nhưng ở đây tôi cần một tham số đó là phụ thuộc vào vị trí nhiều hơn. Bạn có bất kỳ khó khăn khác làm thế nào để mô tả các cạnh?

Tôi đã đính kèm một hình ảnh của rãnh sau khi phát hiện cạnh khóe. Ở đây, các cạnh là các đường thẳng và rãnh là tốt. Rất tiếc, tôi không có quyền truy cập vào các hình ảnh có rãnh bị hỏng tại thời điểm này. Tuy nhiên, trong hình ảnh với rãnh bị hỏng, các đường sẽ có khoảng trống lớn (ít nhất 10% kích thước của hình ảnh) hoặc sẽ không song song.

+0

Có thể bạn có thể bao gồm hình ảnh mẫu? –

+0

Chào mừng bạn đến với Stackoverflow. Hãy cẩn thận xem lại câu trả lời của tôi, sau đó bỏ phiếu nếu nó giúp bạn. Bạn có thể nhấp vào hộp kiểm gần đó để chọn nó làm câu trả lời chính thức cho câu hỏi của bạn. Bằng cách làm công cụ này, bạn sẽ giúp khách truy cập trong tương lai như chính mình và chúng tôi bằng cách giữ cho chuỗi này được tổ chức. – karlphillip

Trả lời

6

Cốt lõi của kỹ thuật Tôi đang chia sẻ dưới đây sử dụng cv::HoughLinesP() để tìm các đoạn đường kẻ trong hình ảnh thang độ xám.

Ứng dụng bắt đầu bằng cách tải hình ảnh đầu vào dưới dạng thang độ xám. Sau đó, nó thực hiện một thao tác tiền xử lý cơ bản để nâng cao đặc điểm nhất định của hình ảnh, nhằm cải thiện việc phát hiện được thực hiện bởi 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); 

Tại thời điểm này, tất cả các đoạn thẳng được phát hiện có thể được viết vào một tập tin để hiển thị mục đích:

Tại thời điểm này chúng ta cần phải sắp xếp vector của chúng ta về đường vì cv::HoughLinesP() không làm điều đó, và chúng ta cần các vector được sắp xếp để có thể xác định các nhóm dòng, bằng cách đo và so sánh khoảng cách giữa các dòng:

/* 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; 

Phần cuối cùng của mã trên chỉ lưu trữ vị trí chỉ số của vectơ của dòng trong vector<int> mới để chúng tôi biết dòng nào bắt đầu một nhóm mới.

Ví dụ: giả sử rằng các chỉ mục được lưu trữ trong vectơ mới là: 0 4 8 12. Hãy nhớ rằng: họ xác định bắt đầu của mỗi nhóm. Điều đó có nghĩa là các dòng kết thúc của các nhóm là: 0, 4-1, 4, 8-1, 8, 12-1, 12.

Biết rằng, chúng ta viết đoạn mã sau:

/* 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; 
} 

Và hình ảnh kết quả là:

Nó không phải là một trận đấu hoàn hảo, nhưng nó gần gũi. Với một chút điều chỉnh ở đây và có cách tiếp cận này có thể nhận được tốt hơn nhiều.Tôi sẽ bắt đầu bằng cách viết một logic thông minh hơn cho sort_by_y_coord, nên loại bỏ các đường có khoảng cách nhỏ giữa các tọa độ X (tức là các đoạn đường nhỏ), và các đường thẳng không được căn chỉnh hoàn hảo trên trục X (giống như đường từ nhóm thứ hai trong hình ảnh đầu ra). Đề xuất này có ý nghĩa hơn sau khi bạn dành thời gian để đánh giá hình ảnh đầu tiên do ứng dụng tạo ra.

Chúc may mắn.

+1

Cảm ơn! Câu trả lời tuyệt vời. – marc

4

Điều gì sẽ xảy ra ngay lập tức với bạn là Hough Transform. Đây là một chương trình bỏ phiếu trong không gian dòng, trong đó có mỗi dòng có thể và cung cấp cho bạn một số điểm cho nó. Trong mã mà tôi đã liên kết ở trên, bạn có thể chỉ cần đặt ngưỡng xấp xỉ ~ 10% các rãnh/đường bị vặn lên.

+1

Nếu các đường thẳng không hoàn toàn song song, hoặc nếu chúng bị cong, thì Marc có thể sử dụng Hough trong các vùng NxN của ảnh để tìm các đường thẳng nối tiếp. Sau đó, các đoạn đường ngắn có thể được nối để tìm đường cong. – Rethunk

Các vấn đề liên quan