2012-11-23 22 views
7

enter image description here Ho immagini di una superficie con molte scanalature. Nella maggior parte dei casi i bordi della scanalatura formano linee parallele in modo tale che la trasformazione di Canny e Hough funzioni molto bene per rilevare le linee e per eseguire una caratterizzazione. Tuttavia, in diversi punti la scanalatura è danneggiata e i bordi non sono più paralleli.Rilevamento di scanalature OpenCV

Sto cercando un modo semplice per verificare se un certo bordo è una linea retta o se ci sono spazi o deviazioni da una linea retta. Sto pensando a qualcosa come il parametro R square nell'interpolazione lineare, ma qui ho bisogno di un parametro che sia più dipendente dalla posizione. Hai qualche altro pensiero su come caratterizzare i bordi?

Ho allegato un'immagine della scanalatura dopo il rilevamento del bordo canny. Qui, i bordi sono linee rette e la scanalatura va bene. Purtroppo non ho accesso alle immagini con scanalature danneggiate al momento. Tuttavia, nelle immagini con scanalature danneggiate, le linee presenterebbero grosse lacune (almeno il 10% delle dimensioni dell'immagine) o non sarebbero parallele.

+0

Forse potresti includere un'immagine campione? –

+0

Benvenuti in Stackoverflow. Per favore controlla attentamente la mia risposta, quindi vota se ti ha aiutato. Puoi cliccare sulla casella accanto per selezionarla come risposta ufficiale alla tua domanda. Facendo queste cose aiuterai i futuri visitatori come te e noi mantenendo questo thread organizzato. – karlphillip

risposta

6

Il nucleo della tecnica Sto condividendo in basso utilizza cv::HoughLinesP() per trovare segmenti di linea in un'immagine in scala di grigi.

L'applicazione inizia caricando l'immagine di input come scala di grigi. Poi esegue un'operazione di base pre-elaborazione per migliorare certe caratteristiche dell'immagine, al fine di migliorare la rilevazione eseguita da 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); 

A questo punto, tutti i segmenti di linea rilevati possono essere scritti in un file per la visualizzazione fini:

a questo punto abbiamo bisogno di ordinare il nostro vettore di linee a causa cv::HoughLinesP() non lo fa, e abbiamo bisogno il vettore allineati per essere in grado di identificare i gruppi di linee, misurando e confrontando il La distanza tra le linee:

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

L'ultima parte del codice precedente semplicemente memorizza le posizioni di indice del vettore di linee in un nuovo vector<int> quindi sappiamo che riveste inizia un nuovo gruppo.

Ad esempio, si supponga che gli indici memorizzati nel nuovo vettore siano: 0 4 8 12. Ricorda: definiscono start di ogni gruppo. Ciò significa che le linee finali dei gruppi sono: 0, 4-1, 4, 8-1, 8, 12-1, 12.

Sapendo che, scriviamo il seguente codice:

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

E l'immagine risultante è:

Non è una partita perfetta, ma è vicino. Con un po 'di modifiche qui e là questo approccio può migliorare molto.Vorrei iniziare scrivendo una logica più intelligente per sort_by_y_coord, che dovrebbe scartare le linee che hanno piccole distanze tra le coordinate X (cioè piccoli segmenti di linea), e anche le linee che non sono perfettamente allineate sull'asse X (come quella del secondo gruppo nell'immagine di output). Questo suggerimento ha molto più senso dopo aver preso il tempo per valutare la prima immagine generata dall'applicazione.

Buona fortuna.

+1

Grazie! Ottima risposta – marc

4

Quello che immediatamente mi viene in mente sarebbe un Hough Transform. Questo è uno schema di voto nello spazio delle linee, che prende ogni linea possibile e ti dà un punteggio per esso. Nel codice che ho collegato sopra, potresti semplicemente impostare una soglia che si avvicini al ~ 10% delle scanalature/linee avvitate.

+1

Se le linee non sono abbastanza parallele o se sono curve, Marc può usare Hough nelle regioni NxN dell'immagine per trovare linee rette a tratti. I segmenti della linea corta potrebbero quindi essere uniti per trovare le curve. – Rethunk

Problemi correlati