it-swarm-eu.dev

Algoritmo per rilevare gli angoli del foglio di carta nella foto

Qual è il modo migliore per rilevare gli angoli di una fattura/ricevuta/foglio di carta in una foto? Questo deve essere usato per la successiva correzione prospettica, prima dell'OCR.

Il mio approccio attuale è stato:

RGB> Grigio> Rilevamento bordi canny con soglia> Dilata (1)> Rimuovi piccoli oggetti (6)> cancella oggetti boarder> seleziona blog di grandi dimensioni in base all'area convessa. > [rilevamento angolo - Non implementato]

Non posso fare a meno di pensare che ci debba essere un approccio "intelligente"/statistico più solido per gestire questo tipo di segmentazione. Non ho molti esempi di allenamento, ma probabilmente potrei mettere insieme 100 immagini.

Contesto più ampio:

Sto usando Matlab per il prototipo e sto pianificando di implementare il sistema in OpenCV e Tesserect-OCR. Questo è il primo di una serie di problemi di elaborazione delle immagini che devo risolvere per questa specifica applicazione. Quindi sto cercando di creare la mia soluzione e familiarizzare con gli algoritmi di elaborazione delle immagini.

Ecco alcune immagini di esempio che vorrei che l'algoritmo gestisse: se desideri raccogliere la sfida, le immagini di grandi dimensioni sono disponibili su http://madteckhead.com/tmp

case 1
(fonte: madteckhead.com )

case 2
(fonte: madteckhead.com )

case 3
(fonte: madteckhead.com )

case 4
(fonte: madteckhead.com )

Nel migliore dei casi questo dà:

case 1 - canny
(fonte: madteckhead.com )

case 1 - post canny
(fonte: madteckhead.com )

case 1 - largest blog
(fonte: madteckhead.com )

Tuttavia fallisce facilmente in altri casi:

case 2 - canny
(fonte: madteckhead.com )

case 2 - post canny
(fonte: madteckhead.com )

case 2 - largest blog
(fonte: madteckhead.com )

Grazie in anticipo per tutte le grandi idee! Amo così!

EDIT: Hough Transform Progress

D: Quale algoritmo raggrupperebbe le linee hough per trovare gli angoli? Seguendo i consigli delle risposte sono stato in grado di utilizzare la trasformazione di Hough, selezionare le linee e filtrarle. Il mio approccio attuale è piuttosto rozzo. Ho ipotizzato che la fattura sarà sempre inferiore a 15 gradi rispetto all'allineamento con l'immagine. Finisco con risultati ragionevoli per le linee se questo è il caso (vedi sotto). Ma non sono del tutto sicuro di un algoritmo adatto per raggruppare le linee (o votare) per estrapolare gli angoli. Le linee di Hough non sono continue. E nelle immagini rumorose, possono esserci linee parallele, quindi sono necessarie alcune metriche di forma o distanza dalla linea. Qualche idea?

case 1 case 2case 3case 4
(fonte: madteckhead.com )

90
Nathan Keller

Sono l'amico di Martin che ci stava lavorando all'inizio di quest'anno. Questo è stato il mio primo progetto di codifica in assoluto, e un po 'è finito in un po' di fretta, quindi il codice ha bisogno di qualche errore ... decodifica ... Ti darò alcuni consigli da quello che ti ho già visto fare, e poi ordina il mio codice per il mio giorno libero domani.

Primo consiglio, OpenCV e python sono fantastici, passa a loro il più presto possibile. : D

Invece di rimuovere piccoli oggetti e/o rumore, abbassa i vincoli astuti, quindi accetta più bordi e quindi trova il contorno chiuso più grande (in OpenCV usa findcontour() con alcuni semplici parametri, penso di aver usato CV_RETR_LIST). potrebbe ancora lottare quando è su un pezzo di carta bianca, ma sicuramente fornisce i migliori risultati.

Per Houghline2() Transform, prova con CV_HOUGH_STANDARD Al contrario di CV_HOUGH_PROBABILISTIC, Ti darà rho e theta valori, definendo la linea in coordinate polari, quindi è possibile raggruppare le linee entro una certa tolleranza a quelle.

Il mio raggruppamento ha funzionato come una tabella di ricerca, per ogni linea emessa dalla trasformazione hough avrebbe dato una coppia rho e theta. Se questi valori erano all'interno, diciamo il 5% di una coppia di valori nella tabella, venivano scartati, se erano al di fuori di quel 5%, una nuova voce veniva aggiunta alla tabella.

È quindi possibile eseguire l'analisi di linee parallele o la distanza tra le linee molto più facilmente.

Spero che sia di aiuto.

26
Daniel Crowley

Un gruppo di studenti della mia università ha recentemente dimostrato un'app per iPhone (e python) che avevano scritto per fare esattamente questo. Come ricordo, i passaggi erano qualcosa del genere:

  • Filtro mediano per rimuovere completamente il testo sulla carta (questo era un testo scritto a mano su carta bianca con un'illuminazione abbastanza buona e potrebbe non funzionare con il testo stampato, ha funzionato molto bene). Il motivo è che rende molto più facile il rilevamento degli angoli.
  • Trasformazione di Hough per linee
  • Trova i picchi nello spazio dell'accumulatore Hough Transform e traccia ogni linea sull'intera immagine.
  • Analizza le linee e rimuovi quelle molto vicine tra loro e con un'angolazione simile (raggruppa le linee in una). Ciò è necessario perché la trasformazione di Hough non è perfetta in quanto funziona in uno spazio di campionamento discreto.
  • Trova coppie di linee che sono approssimativamente parallele e che si intersecano con altre coppie per vedere quali linee formano quadruple.

Sembrava funzionare abbastanza bene e sono stati in grado di scattare una foto di un pezzo di carta o di un libro, eseguire il rilevamento degli angoli e quindi mappare il documento nell'immagine su un piano piatto in quasi tempo reale (c'era una singola funzione OpenCV da eseguire la mappatura). Non c'era OCR quando l'ho visto funzionare.

17
Martin Foot

Ecco cosa mi è venuto in mente dopo un po 'di sperimentazione:

import cv, cv2, numpy as np
import sys

def get_new(old):
    new = np.ones(old.shape, np.uint8)
    cv2.bitwise_not(new,new)
    return new

if __== '__main__':
    orig = cv2.imread(sys.argv[1])

    # these constants are carefully picked
    MORPH = 9
    CANNY = 84
    HOUGH = 25

    img = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
    cv2.GaussianBlur(img, (3,3), 0, img)


    # this is to recognize white on white
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(MORPH,MORPH))
    dilated = cv2.dilate(img, kernel)

    edges = cv2.Canny(dilated, 0, CANNY, apertureSize=3)

    lines = cv2.HoughLinesP(edges, 1,  3.14/180, HOUGH)
    for line in lines[0]:
         cv2.line(edges, (line[0], line[1]), (line[2], line[3]),
                         (255,0,0), 2, 8)

    # finding contours
    contours, _ = cv2.findContours(edges.copy(), cv.CV_RETR_EXTERNAL,
                                   cv.CV_CHAIN_APPROX_TC89_KCOS)
    contours = filter(lambda cont: cv2.arcLength(cont, False) > 100, contours)
    contours = filter(lambda cont: cv2.contourArea(cont) > 10000, contours)

    # simplify contours down to polygons
    rects = []
    for cont in contours:
        rect = cv2.approxPolyDP(cont, 40, True).copy().reshape(-1, 2)
        rects.append(rect)

    # that's basically it
    cv2.drawContours(orig, rects,-1,(0,255,0),1)

    # show only contours
    new = get_new(img)
    cv2.drawContours(new, rects,-1,(0,255,0),1)
    cv2.GaussianBlur(new, (9,9), 0, new)
    new = cv2.Canny(new, 0, CANNY, apertureSize=3)

    cv2.namedWindow('result', cv2.WINDOW_NORMAL)
    cv2.imshow('result', orig)
    cv2.waitKey(0)
    cv2.imshow('result', dilated)
    cv2.waitKey(0)
    cv2.imshow('result', edges)
    cv2.waitKey(0)
    cv2.imshow('result', new)
    cv2.waitKey(0)

    cv2.destroyAllWindows()

Non perfetto, ma almeno funziona per tutti i campioni:

1234

16
Vanuan

Invece di iniziare dal rilevamento dei bordi, è possibile utilizzare il rilevamento degli angoli.

Marvin Framework fornisce un'implementazione dell'algoritmo Moravec per questo scopo. Potresti trovare gli angoli dei fogli come punto di partenza. Di seguito l'output dell'algoritmo di Moravec:

enter image description here

Inoltre puoi usare MSER (Regioni estreme massimamente stabili) sul risultato dell'operatore Sobel per trovare le regioni stabili dell'immagine. Per ogni regione restituita da MSER è possibile applicare lo scafo convesso e l'approssimazione di poli per ottenere alcuni come questo:

Ma questo tipo di rilevamento è utile per il rilevamento dal vivo più di una singola immagine che non sempre restituisce il risultato migliore.

result

4
Flayn

Qui hai il codice di @Vanuan usando C++:

cv::cvtColor(mat, mat, CV_BGR2GRAY);
cv::GaussianBlur(mat, mat, cv::Size(3,3), 0);
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Point(9,9));
cv::Mat dilated;
cv::dilate(mat, dilated, kernel);

cv::Mat edges;
cv::Canny(dilated, edges, 84, 3);

std::vector<cv::Vec4i> lines;
lines.clear();
cv::HoughLinesP(edges, lines, 1, CV_PI/180, 25);
std::vector<cv::Vec4i>::iterator it = lines.begin();
for(; it!=lines.end(); ++it) {
    cv::Vec4i l = *it;
    cv::line(edges, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(255,0,0), 2, 8);
}
std::vector< std::vector<cv::Point> > contours;
cv::findContours(edges, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_TC89_KCOS);
std::vector< std::vector<cv::Point> > contoursCleaned;
for (int i=0; i < contours.size(); i++) {
    if (cv::arcLength(contours[i], false) > 100)
        contoursCleaned.Push_back(contours[i]);
}
std::vector<std::vector<cv::Point> > contoursArea;

for (int i=0; i < contoursCleaned.size(); i++) {
    if (cv::contourArea(contoursCleaned[i]) > 10000){
        contoursArea.Push_back(contoursCleaned[i]);
    }
}
std::vector<std::vector<cv::Point> > contoursDraw (contoursCleaned.size());
for (int i=0; i < contoursArea.size(); i++){
    cv::approxPolyDP(Mat(contoursArea[i]), contoursDraw[i], 40, true);
}
Mat drawing = Mat::zeros( mat.size(), CV_8UC3 );
cv::drawContours(drawing, contoursDraw, -1, cv::Scalar(0,255,0),1);
3
GBF_Gabriel

Dopo il rilevamento dei bordi, utilizzare Hough Transform. Quindi, inserisci quei punti in una SVM (macchina vettoriale di supporto) con le loro etichette, se gli esempi hanno linee morbide su di essi, SVM non avrà alcuna difficoltà a dividere le parti necessarie dell'esempio e altre parti. Il mio consiglio su SVM, metto un parametro come connettività e lunghezza. Cioè, se i punti sono collegati e lunghi, è probabile che siano una riga della ricevuta. Quindi, puoi eliminare tutti gli altri punti.

3
Hephaestus
  1. Quindi utilizzare i contorni o il segno su uno dei cluster (interno)

  2. Use kmeans segment 2 cluster

  3. Then use contours or hough on one of the clusters (intenral)
1
user3452134