2015-12-10 8 views
7

Questo è il mio primo programma openCV, quindi sii perdonato se sembro ignorante di alcuni concetti di base della visione artificiale.Estrazione di colonne giornaliere semirigide

UPDATE: Vedere nuovo codice/nuovo problema, in fondo, grazie alla risposta da sturkmen

Sto lavorando su "digitalizzazione" un grande insieme di immagini, come quelle attaccate, come un progetto. Tutte le immagini provengono dalla stessa fonte. L'obiettivo finale è quello di passare pezzi di testo estratti a tesseract, la libreria OCR.

(codice sorgente in basso) ho intenzione di spiegare il mio approccio attuale, e quindi dichiarare le mie domande.

Il mio approccio attuale è la seguente:

  • immagine Dilate

    1. Applicare inversa soglia binario e trovare contorni

    2. Creare un boundingRect da ciascun contorno, poi filtrare per minimo e dimensioni massime

    questo ha funzionato ok

    mio risultato finale desiderato è quello di avere uno boundingRect intorno ogni colonna. Quindi per le foto fornite che sarebbero sette di loro.

    Quindi, il problema è che le "mini sezioni" tabulate nell'immagine non vengono rilevate in modo affidabile (l'esempio migliore sarebbe quello nella colonna all'estrema destra che non ha uno boundingRect attorno).

    Posso pensare a due possibili soluzioni (in modo da non essere una domanda di tipo a risposta aperta) ma se si conosce una soluzione migliore condividerla!

    1) combinare boundingRect s che sono vicini verticali per acquisire le colonne. Contiene possibili errori del caso limite.

    2) Trovare un modo diverso di manipolare l'immagine prima di trovare i contorni. Dalla mia ricerca, l'algoritmo di livellamento della lunghezza della corsa sembra promettente?

    Quindi la mia domanda è: quale approccio è il migliore? Ho trascurato una soluzione migliore? Sono inesperto in questo reparto, quindi nessun suggerimento è troppo piccolo.

    Grazie per la lettura!

    #include "opencv2/core.hpp" 
    #include "opencv2/highgui.hpp" 
    #include "opencv2/imgproc.hpp" 
    #include <iostream> 
    #include <fstream> 
    #include <sstream> 
    #include <vector> 
    
    using namespace cv; 
    using namespace std; 
    
    int main(int argc, char* argv[]) 
    { 
        Mat image = imread(path_to_file); 
        Mat gray; 
        cvtColor(image, gray, COLOR_BGR2GRAY); 
        Mat fin; 
        double thresh = threshold(gray, fin, 160, 255, THRESH_BINARY_INV); 
        //size impacts dilation 
        Mat kernel = getStructuringElement(MORPH_CROSS, Size(2, 4)); 
        Mat dilated; 
        dilate(fin, dilated, kernel, Point(-1,-1), 6); 
        imwrite("testbw.png",dilated); 
        Mat hierarchy; 
        vector<vector<Point> >contours; 
        findContours(dilated, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE); 
    
        //potentially sort by x 
        for (const auto& c : contours) 
        { 
         //  x  y 
         //columns 850 x 5400 
         Rect r = boundingRect(c); 
         if (r.height > 3000 || r.width > 875) 
          continue; 
         if (r.height < 100 || r.width < 500) 
          continue; 
    
         rectangle(image, r, Scalar(255, 0, 255), 2); //made thicker 
        } 
        imwrite("test.png", image); 
    
        waitKey(0); 
        return 0; 
    

    }

    Immagine originale:

    codice aggiornato

    int main(int argc, char* argv[]) 
    { 
        Mat image = imread(path_to_file); 
        Mat gray; 
        cvtColor(image, gray, COLOR_BGR2GRAY); 
        Mat fin; 
        double thresh = threshold(gray, fin, 160, 255, THRESH_BINARY_INV); 
    
        Mat kernel = getStructuringElement(MORPH_CROSS, Size(2, 4)); 
        Mat dilated; 
        dilate(fin, dilated, kernel, Point(-1,-1), 6); 
        vector<Vec4i> hierarchy; 
        vector<vector<Point> >contours; 
        findContours(dilated, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE); 
    
        vector<Rect> rects; 
        Rect big_rect = Rect(image.cols/2,image.rows/2,1,1); 
    
        for (const auto& c : contours) 
        { 
         //  x  y 
         //columns 850 x 5400 
         Rect r = boundingRect(c); 
         if (r.height > 5500 || r.width > 875) 
          continue; 
         if (r.height < 300 || r.width < 500) 
          continue; 
    
         big_rect = big_rect | r; // here we will find bounding box of all Rects 
         rects.push_back(r); // stores rects 
        } 
    
        for (size_t i = 0; i < rects.size(); i++) 
        { 
         // sets y and height of all rects 
         //cout << rects[i].x << endl; 
         rects[i].y = big_rect.y; 
         rects[i].height = big_rect.height; 
        } 
    
        //groupRectangles(rects, 1); DIDN'T WORK 
    
        for (size_t i = 0; i < rects.size(); i++) 
        { 
         rectangle(image, rects[i], Scalar(255, 0, 255), 2); 
        } 
        imshow("test", image); 
    

    nuovo risultato:

    nuovo problema: Ci sono molti boundingRect s attorno a ogni colonna (probabilmente non si può dire guardando l'immagine). Questo è un problema, perché voglio fare una sotto-immagine di ogni colonna, ad es. Mat ROI = image(rects[i]) che renderebbe molto più delle 7 immagini desiderate.

    Nuova domanda: Come posso combinare la moltitudine di rettangoli per colonna in uno? Ho visto OpenCV groupRectangles, ma non funzionava.

  • +0

    Quello che voglio è una 'boundingRect' per colonna. Funzionerebbe anche una soluzione che catturasse in modo affidabile i quadrati tabulati. Inizialmente pensavo che sarebbe stato più semplice avere un 'boundingRect' per colonna, che avrebbe contenuto solo quella colonna. Perché da lì creerò (in questo caso 7) sotto-immagini (una per colonna) e poi le passerò a tesseract per OCR. Lavorerò al caricamento dell'immagine originale ora –

    +1

    Hai bisogno di un metodo che raggruppa i rettangoli che hai adesso in 7 colonne? O qualcosa che rileva direttamente le colonne complete? – Miki

    +0

    Ecco dove sono aperto ai suggerimenti, scusa se ciò non è molto utile. Quale sceglieresti? Finché riesco a catturare in modo affidabile quasi ogni parola nell'immagine, il mio obiettivo è raggiunto. Penso che se i rettangoli fossero raggruppati verticalmente (in modo da separare le colonne), allora ci sarebbero casi limite in cui fallirebbe, che menziono nella domanda. –

    risposta

    1

    solo per mostrare un metodo ho provato a cambiare il codice come di seguito.

    #include "opencv2/core.hpp" 
    #include "opencv2/highgui.hpp" 
    #include "opencv2/imgproc.hpp" 
    #include <iostream> 
    #include <fstream> 
    #include <sstream> 
    #include <vector> 
    
    using namespace cv; 
    using namespace std; 
    
    int main(int argc, char* argv[]) 
    { 
        Mat image = imread(argv[1]); 
        Mat gray; 
        cvtColor(image, gray, COLOR_BGR2GRAY); 
        Mat fin; 
        double thresh = threshold(gray, fin, 160, 255, THRESH_BINARY_INV); 
        //size impacts dilation 
        Mat kernel = getStructuringElement(MORPH_CROSS, Size(2, 4)); 
        Mat dilated; 
        dilate(fin, dilated, kernel, Point(-1,-1), 1); 
        imwrite("testbw.png",dilated); 
        Mat hierarchy; 
        vector<vector<Point> >contours; 
        findContours(dilated, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE); 
    
        vector<Rect> rects; 
        Rect big_rect = Rect(image.cols/2,image.rows/2,1,1); 
        //potentially sort by x 
        for (const auto& c : contours) 
        { 
         //  x  y 
         //columns 850 x 5400 
         Rect r = boundingRect(c); 
         if (r.height > 3000 || r.width > 875) 
          continue; 
         if (r.height < 10 || r.width < 10) // changed for test small image 
          continue; 
    
         big_rect = big_rect | r; // here we will find bounding box of all Rects 
         rects.push_back(r); // stores rects 
        } 
    
        for (size_t i = 0; i < rects.size(); i++) 
        { 
         // sets y and height of all rects 
         rects[i].y = big_rect.y; 
         rects[i].height = big_rect.height; 
        } 
    
        for (size_t i = 0; i < rects.size(); i++) 
        { 
         rectangle(image, rects[i], Scalar(255, 0, 255), 2); 
        } 
    
        imshow("result", image); 
    
        waitKey(0); 
        return 0; 
    } 
    

    Test Image enter image description here

    So che è incompleta, ma spero che capire il modo e completarlo filtrando i rettangoli di trovare desiderato sette rett o ti completare il codice al più presto.

    MODIFICA: il codice può essere un po 'sporco ma vector<Rect> final_rects contiene solo le postazioni necessarie.

    #include "opencv2/core.hpp" 
    #include "opencv2/highgui.hpp" 
    #include "opencv2/imgproc.hpp" 
    #include <iostream> 
    #include <fstream> 
    #include <sstream> 
    #include <vector> 
    
    using namespace cv; 
    using namespace std; 
    
    struct sorter_func 
    { 
        bool operator()(Rect a, Rect b) 
        { 
         return a.x < b.x; 
        } 
    }; 
    
    int main(int argc, char* argv[]) 
    { 
        Mat image = imread(argv[1]); 
        Mat gray; 
        cvtColor(image, gray, COLOR_BGR2GRAY); 
        Mat fin; 
        double thresh = threshold(gray, fin, 160, 255, THRESH_BINARY_INV); 
        //size impacts dilation 
        Mat kernel = getStructuringElement(MORPH_CROSS, Size(2, 4)); 
        Mat dilated; 
        dilate(fin, dilated, kernel, Point(-1,-1), 1); 
        imwrite("testbw.png",dilated); 
        Mat hierarchy; 
        vector<vector<Point> >contours; 
        findContours(dilated, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE); 
    
        vector<Rect> rects; 
        Rect big_rect = Rect(image.cols/2,image.rows/2,1,1); 
        //potentially sort by x 
        for (const auto& c : contours) 
        { 
         //  x  y 
         //columns 850 x 5400 
         Rect r = boundingRect(c); 
         if (r.height > 3000 || r.width > 875) 
          continue; 
         if (r.height < 10 || r.width < 10) // changed for test small image 
          continue; 
    
         big_rect = big_rect | r; // here we will find bounding box of all Rects 
         rects.push_back(r); // stores rects 
        } 
    
        for (size_t i = 0; i < rects.size(); i++) 
        { 
         // sets y and height of all rects 
         rects[i].y = big_rect.y; 
         rects[i].height = big_rect.height; 
        } 
    
        std::sort(rects.begin(), rects.end(), sorter_func()); 
    
        for (size_t i = 1; i < rects.size(); i++) 
        { 
         Rect big_rect = rects[i-1] | rects[i]; 
         if(big_rect.width < rects[i-1].width + rects[i].width) 
         { 
          rects[i-1] = Rect(); 
          rects[i] = big_rect; 
         } 
        } 
    
        vector<Rect> final_rects; 
        for (size_t i = 1; i < rects.size(); i++) 
        { 
         if(rects[i].width > 0) 
         { 
          rectangle(image, rects[i], Scalar(rand()&255,rand()&255,rand()&255), 2); 
          final_rects.push_back(rects[i]); 
          cerr << final_rects.size() << endl; 
         } 
        } 
        imshow("result", image); 
        waitKey(0); 
        return 0; 
    } 
    

    enter image description here

    +0

    Ciao! Questo sembra essere un ottimo approccio. Ho adattato il tuo codice un po 'ieri sera, e avvolge perfettamente le colonne, ma sto combattendo una cosa .. Ci sono molti (probabilmente circa 5-10) 'boundingRect's attorno a ogni colonna. Quindi dovrò eliminare tutti tranne uno, perché il mio piano è quello di creare una sottoimmagine da ciascun 'boundingRect'. Aggiornerò la mia domanda –

    +0

    @ Trés DuBiel ho aggiornato il codice. fammi sapere se hai bisogno di aiuto per capire il codice. – sturkmen

    +0

    molto bello!Sono venuto a una soluzione separata con lo stesso risultato, ma mi piace il tuo meglio! Sembra più robusto. Penso di avere l'idea del tuo codice, proverò a sintetizzarlo. Si ordinano i rects in ordine ascendente (in base al valore x), l'OR bit a bit '|' Non riesco a capirmi del tutto ma il risultato di esso lascia solo il 'Rect' più grande per colonna, lasciando il' Rect' più piccolo per colonna uguale a 'Rect()'. Poi da lì si filtrano tutti i 'Rects' che non hanno' width'. Se hai tempo, spiega un po 'l'operazione '|' –