Algorytm wykrywania narożników arkusza papieru na zdjęciu

Jaki jest najlepszy sposób na wykrycie narożników faktury / paragonu/kartki papieru na zdjęciu? Ma to być wykorzystane do późniejszej korekcji perspektywy, przed OCR.

Moje obecne podejście było:

RGB > Szary > wykrywanie krawędzi z progami > Rozszerzanie (1) > usuwanie małych obiektów(6) > Usuwanie obiektów granicznych > wybieranie dużych blogów na podstawie obszaru wypukłego. > [wykrywanie narożników - nie zaimplementowane]

Nie mogę się powstrzymać, ale myślę, że musi być mocniejszy "inteligentne" / statystyczne podejście do obsługi tego typu segmentacji. Nie mam zbyt wielu przykładów treningowych, ale prawdopodobnie mógłbym zebrać razem 100 zdjęć.

Szerszy kontekst:

Używam Matlaba do prototypowania i planuję zaimplementować system w OpenCV i TESSERECT-OCR. Jest to pierwszy z wielu problemów przetwarzania obrazu, które muszę rozwiązać dla tej konkretnej aplikacji. Szukam więc własnego rozwiązania i ponownego zapoznania się z przetwarzaniem obrazu algorytmy.

Oto przykładowe zdjęcie, które chciałbym, aby algorytm obsłużył: jeśli chcesz podjąć wyzwanie, duże obrazy są na http://madteckhead.com/tmp

Przypadek 1 http://madteckhead.com/tmp/IMG_0773_sml.jpg przypadek 2 http://madteckhead.com/tmp/IMG_0774_sml.jpg przypadek 3 http://madteckhead.com/tmp/IMG_0775_sml.jpg przypadek 4 http://madteckhead.com/tmp/IMG_0776_sml.jpg

W najlepszym przypadku to daje:

Case 1-canny http://madteckhead.com/tmp/IMG_0773_canny.jpg case 1-post canny http://madteckhead.com/tmp/IMG_0773_postcanny.jpg case 1-największy blog http://madteckhead.com/tmp/IMG_0773_blob.jpg

Jednak łatwo zawodzi w innych przypadkach:

Case 2-canny http://madteckhead.com/tmp/IMG_0774_canny.jpg case 2-post canny http://madteckhead.com/tmp/IMG_0774_postcanny.jpg case 2-największy blog http://madteckhead.com/tmp/IMG_0774_blob.jpg

Z góry dziękuję za wszystkie świetne pomysły! Kocham tak!

EDIT: Hough Transform Progress

P: jaki algorytm mógłby gromadzić linie hougha, aby znaleźć narożniki? Za radą odpowiedzi udało mi się użyć transformacji Hougha, wybrać linie i je filtrować. Moje obecne podejście jest raczej prymitywne. Założyłem, że faktura będzie zawsze mniejsza niż 15deg z wyrównaniem do obrazu. Kończę z rozsądnym wyniki dla wierszy, jeśli tak jest (patrz poniżej). Ale nie jestem całkowicie pewien odpowiedniego algorytmu do klastra linii (lub głosowania) ekstrapolacji dla narożników. Linie Hougha nie są ciągłe. A w hałaśliwych obrazach mogą występować równoległe linie, więc wymagana jest pewna forma lub odległość od metryk pochodzenia linii. Jakieś pomysły?

Przypadek 1 http://madteckhead.com/tmp/IMG_0773_hough.jpg przypadek 2 http://madteckhead.com/tmp/IMG_0774_hough.jpg przypadek 3 http://madteckhead.com/tmp/IMG_0775_hough.jpg przypadek 4 http://madteckhead.com/tmp/IMG_0776_hough.jpg

Author: Nathan Keller, 2011-07-02

8 answers

Jestem przyjacielem Martina, który pracował nad tym wcześniej w tym roku. To był mój pierwszy projekt kodowania, i trochę zakończył się w pośpiechu, więc kod potrzebuje trochę errr...dekodowanie... Dam kilka wskazówek z tego, co już widziałem, a potem uporządkuję mój kod w dniu wolnym jutro.

Pierwsza wskazówka, OpenCV i python są niesamowite, przenieś się do nich jak najszybciej. : D

Zamiast usuwać małe przedmioty i szum, obniż ograniczniki canny, aby akceptował więcej krawędzi i następnie Znajdź największy zamknięty kontur (w OpenCV użyj {[2] } z prostymi parametrami, chyba użyłem CV_RETR_LIST). może nadal walczyć, gdy jest na białym kartce papieru, ale zdecydowanie zapewnia najlepsze wyniki.

Dla transformaty Houghline2() spróbuj użyć CV_HOUGH_STANDARD W przeciwieństwie do CV_HOUGH_PROBABILISTIC, da ona Rho i theta wartości, definiując linię we współrzędnych biegunowych, a następnie możesz grupować linie w pewnej tolerancji do nich.

Moje ugrupowanie działało jako spójrz w górę tabeli, dla każdej linii wyprowadzonej z transformacji hougha dałaby parę rho i theta. Jeśli wartości te mieściły się w granicach, powiedzmy 5% pary wartości w tabeli, zostały one odrzucone, jeśli znajdowały się poza tym 5%, Dodano nowy wpis do tabeli.

Można wtedy znacznie łatwiej wykonać analizę równoległych linii lub odległości między liniami.

Mam nadzieję, że to pomoże.
 24
Author: Daniel Crowley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-27 17:35:54

Grupa studentów na mojej uczelni zademonstrowała niedawno aplikację na iPhone ' a (i aplikację OpenCV w Pythonie), którą napisali, aby dokładnie to zrobić. Z tego co pamietam to kroki byly cos takiego:

  • Środkowy filtr, aby całkowicie usunąć tekst na papierze(był to odręczny tekst na białym papierze z dość dobrym oświetleniem i może nie działać z drukowanym tekstem, działał bardzo dobrze). Powodem było to, że znacznie ułatwia wykrywanie narożników.
  • transformata Hougha dla linie
  • Znajdź wierzchołki w przestrzeni Hough Transform i narysuj każdą linię na całym obrazie.
  • przeanalizuj linie i usuń wszystkie, które są bardzo blisko siebie i są pod podobnym kątem(połącz linie w jedną). Jest to konieczne, ponieważ transformata Hougha nie jest idealna, ponieważ działa w dyskretnej przestrzeni próbek.
  • Znajdź pary linii, które są mniej więcej równoległe i przecinają inne pary, aby zobaczyć, które linie tworzą quady.

To wydawało się, że działają dość dobrze i byli w stanie zrobić zdjęcie kawałka papieru lub książki, wykonać detekcję narożników, a następnie zmapować dokument na obrazie na płaską płaszczyznę w prawie czasie rzeczywistym (do mapowania była pojedyncza funkcja OpenCV). Nie było OCR, kiedy zobaczyłem, że działa.

 17
Author: Martin Foot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-02 08:03:24

Oto co wymyśliłem po kilku eksperymentach:

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 __name__ == '__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()

Nie idealne, ale przynajmniej działa dla wszystkich próbek:

1234

 16
Author: Vanuan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-20 02:42:09

Zamiast rozpoczynać od detekcji krawędzi można użyć detekcji narożników.

Marvin Framework dostarcza do tego celu implementację algorytmu Moravec. Jako punkt wyjścia można było znaleźć narożniki gazet. Poniżej wyjście algorytmu Moravec ' a:

Tutaj wpisz opis obrazka

 8
Author: Gabriel Ambrósio Archanjo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-27 19:16:58

Możesz również użyć MSER (maksymalnie stabilne ekstremalne regiony) nad operatorem Sobel result, aby znaleźć stabilne regiony obrazu. Dla każdego obszaru zwracanego przez MSER można zastosować wypukły kadłub i przybliżenie poly, aby otrzymać takie oto:

Ale ten rodzaj detekcji jest przydatny do wykrywania NA ŻYWO więcej niż jeden obraz, który nie zawsze zwraca najlepszy wynik.

wynik

 4
Author: Flayn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-17 10:36:57

Po wykryciu krawędzi użyj transformacji Hougha. Następnie umieść te punkty w SVM (supporting vector machine) z ich etykietami, jeśli przykłady mają gładkie linie na nich, SVM nie będzie miał żadnych trudności z podziałem niezbędnych części przykładu i innych części. Moja rada na SVM, umieścić parametr jak łączność i długość. Oznacza to, że jeśli punkty są połączone i długie, prawdopodobnie będą linią odbioru. Następnie możesz wyeliminować wszystkie pozostałe punkty.

 3
Author: Hephaestus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-02 22:17:31

Tutaj masz kod @ Vanuan używając 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
Author: GBF_Gabriel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-09 21:09:42
  1. Convert to lab space

  2. Use kmeans segment 2 cluster

  3. następnie użyj konturów lub hough na jednym z klastrów (intenral)
 1
Author: user3452134,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-29 07:24:19