Algorithme pour détecter les coins de la feuille de papier en photo

Quel est le meilleur moyen de détecter les coins d'une facture / reçu / feuille de papier dans une photo? Ceci doit être utilisé pour la correction de perspective ultérieure, avant OCR.

Mon approche actuelle a été:

RGB > Gris > détection de bord Canny avec seuillage > dilater(1) > Supprimer les petits objets(6) > Effacer les objets de la frontière > choisissez les grands blog en fonction de la zone convexe. > [détection de coin - non implémentée]

Je ne peux pas m'empêcher de penser qu'il doit y avoir un plus robuste approche'intelligente' /statistique pour gérer ce type de segmentation. Je n'ai pas beaucoup d'exemples, mais je pourrais probablement obtenir 100 images.

Contexte plus Large:

J'utilise matlab pour prototyper, et je prévois d'implémenter le système dans OpenCV et Tesserect-OCR. C'est le premier d'un certain nombre de problèmes de traitement d'image que je dois résoudre pour cette application spécifique. Je cherche donc à lancer ma propre solution et à me familiariser avec le traitement d'image algorithme.

Voici un exemple d'image que j'aimerais que l'algorithme gère: si vous souhaitez relever le défi, les grandes images sont à http://madteckhead.com/tmp

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

Dans le meilleur des cas, cette donne:

Cas 1 - futé http://madteckhead.com/tmp/IMG_0773_canny.jpg cas 1 - post futé http://madteckhead.com/tmp/IMG_0773_postcanny.jpg Cas 1-plus grand blog http://madteckhead.com/tmp/IMG_0773_blob.jpg

Cependant, il échoue facilement sur d'autres cas:

Cas 2 - futé http://madteckhead.com/tmp/IMG_0774_canny.jpg cas 2-post canny http://madteckhead.com/tmp/IMG_0774_postcanny.jpg cas 2-plus grand blog http://madteckhead.com/tmp/IMG_0774_blob.jpg

Merci d'avance pour toutes les bonnes idées! J'aime TELLEMENT!

EDIT: progression de la transformation Hough

Q: quel algorithme regrouperait les lignes de hough pour trouver des coins? Suivant les conseils des réponses, j'ai pu utiliser la transformation Hough, choisir des lignes et les filtrer. Mon approche actuelle est plutôt grossière. J'ai fait l'hypothèse que la facture sera toujours inférieure à 15deg hors alignement avec l'image. Je finis avec raisonnable résultats pour lignes si c'est le cas (voir ci-dessous). Mais je ne suis pas tout à fait sûr d'un algorithme approprié pour regrouper les lignes (ou voter) pour extrapoler pour les coins. Les lignes de Hough ne sont pas continues. Et dans les images bruyantes, il peut y avoir des lignes parallèles, de sorte qu'une forme ou une distance par rapport aux métriques d'origine de ligne sont nécessaires. Des idées?

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

83
demandé sur Nathan Keller 2011-07-02 11:12:23

8 réponses

Je suis L'ami de Martin qui travaillait sur ça plus tôt cette année. Ce fut mon premier projet de codage, et un peu terminé dans un peu d'une ruée, de sorte que le code a besoin d'un certain errr...décoder... Je vais donner quelques conseils de ce que je vous ai déjà vu faire, puis trier mon code sur mon jour de congé demain.

Premier conseil, OpenCV et python sont géniaux, déplacez-vous vers eux dès que possible. : D

Au lieu d'enlever les petits objets et ou le bruit, abaissez les contraintes canny, de sorte qu'il accepte plus de bords, et ensuite, trouvez le plus grand contour fermé (dans OpenCV utilisez findcontour() avec quelques paramètres simples, je pense que j'ai utilisé CV_RETR_LIST). pourrait encore lutter quand il est sur un morceau de papier blanc, mais était certainement fournir les meilleurs résultats.

Pour la transformation Houghline2(), essayez avec le CV_HOUGH_STANDARD par opposition au CV_HOUGH_PROBABILISTIC, Il donnera rhoet THETA Valeurs, définissant la ligne en coordonnées polaires, et ensuite vous pouvez regrouper les lignes dans une certaine tolérance à celles-ci.

Mon regroupement a fonctionné comme un recherchez la table, pour chaque ligne sortie de la transformation hough, elle donnerait une paire rho et thêta. Si ces valeurs étaient à l'intérieur, disons 5% d'une paire de valeurs dans la table, elles ont été ignorées, si elles étaient en dehors de ce 5%, une nouvelle entrée a été ajoutée à la table.

Vous pouvez ensuite faire l'analyse des lignes parallèles ou de la distance entre les lignes beaucoup plus facilement.

J'espère que cela aide.

24
répondu Daniel Crowley 2016-02-27 17:35:54

Un groupe d'étudiants de mon université a récemment démontré une application iPhone (et une application Python OpenCV) qu'ils avaient écrite pour faire exactement cela. Si je me souviens bien, les étapes étaient quelque chose comme ceci:

  • Filtre médian pour supprimer complètement le texte sur le papier (ce texte était écrit à la main sur du papier blanc avec un éclairage assez bon et peut ne pas fonctionner avec du texte imprimé, il a très bien fonctionné). La raison en était que cela rend la détection de coin beaucoup plus facile.
  • transformation Hough pour lignes
  • Trouvez les pics dans L'espace de L'accumulateur Hough Transform et tracez chaque ligne sur toute l'image.
  • analysez les lignes et supprimez celles qui sont très proches les unes des autres et qui sont à un angle similaire (regroupez les lignes en une seule). Ceci est nécessaire car la transformation Hough n'est pas parfaite car elle fonctionne dans un espace d'échantillon discret.
  • trouvez des paires de lignes qui sont à peu près parallèles et qui croisent d'autres paires pour voir quelles lignes forment des quads.

Ce semblait fonctionner assez bien et ils ont pu prendre une photo d'un morceau de papier ou d'un livre, effectuer la détection de coin, puis mapper le document dans l'image sur un plan plat en presque temps réel (il y avait une seule fonction OpenCV pour effectuer le mappage). Il n'y avait pas D'OCR quand je l'ai vu fonctionner.

17
répondu Martin Foot 2011-07-02 08:03:24

Voici ce que j'ai trouvé après un peu d'expérimentation:

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()

Pas parfait, mais au moins fonctionne pour tous les échantillons:

1234

16
répondu Vanuan 2013-09-20 02:42:09

Au lieu de partir de la détection de bord, vous pouvez utiliser la détection de coin.

Marvin Framework fournit une implémentation de l'algorithme Moravec à cet effet. Vous pouvez trouver les coins des papiers comme point de départ. En dessous de la sortie de l'algorithme de Moravec:

entrez la description de l'image ici

8
répondu Gabriel Ambrósio Archanjo 2013-05-27 19:16:58

Vous pouvez également utiliser MSER (régions extrêmes stables au maximum) sur le résultat de l'opérateur Sobel pour trouver les régions stables de l'image. Pour chaque région renvoyée par MSER, vous pouvez appliquer une approximation de coque convexe et de poly pour en obtenir un peu comme ceci:

Mais ce type de détection est utile pour la détection en direct plus d'une seule image qui ne renvoie pas toujours le meilleur résultat.

résultat

4
répondu Flayn 2015-08-17 10:36:57

Après la détection des arêtes, utilisez la transformation Hough. Ensuite, mettez ces points dans une SVM (supporting vector machine) avec leurs étiquettes, si les exemples ont des lignes lisses sur eux, SVM n'aura aucune difficulté à diviser les parties nécessaires de l'exemple et d'autres parties. Mon conseil sur SVM, mettre un paramètre comme la connectivité et la longueur. Autrement dit, si les points sont connectés et longs, ils sont susceptibles d'être une ligne de la réception. Ensuite, vous pouvez éliminer tous les autres points.

3
répondu Hephaestus 2011-07-02 22:17:31

Ici, vous avez le code de @ Vanuan en utilisant 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
répondu GBF_Gabriel 2016-10-09 21:09:42
  1. Convertir en espace laboratoire

  2. Utilisation kmeans segment 2 cluster

  3. ensuite, utilisez des contours ou hough sur l'un des clusters (intenral)
1
répondu user3452134 2014-10-29 07:24:19