web-dev-qa-db-fra.com

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

Quelle est la meilleure façon de détecter les coins d'une facture/reçu/feuille de papier dans une photo? Ceci doit être utilisé pour une correction de perspective ultérieure, avant l'OCR.

Mon approche actuelle a été:

RVB> Gris> Détection Canny Edge avec seuillage> Dilater (1)> Supprimer les petits objets (6)> effacer les objets internes> choisir un blog large basé sur la zone convexe. > [détection de coin - Non implémenté]

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

Contexte plus large:

J'utilise matlab pour prototyper et je prévois d'implémenter le système dans OpenCV et Tesserect-OCR. Il s'agit du 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 les algorithmes de traitement d'image.

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

case 1
(source: madteckhead.com )

case 2
(source: madteckhead.com )

case 3
(source: madteckhead.com )

case 4
(source: madteckhead.com )

Dans le meilleur des cas, cela donne:

case 1 - canny
(source: madteckhead.com )

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

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

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

case 2 - canny
(source: madteckhead.com )

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

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

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

EDIT: Hough Transform Progress

Q: Quel algorithme regrouperait les lignes de Hough pour trouver des coins? En suivant les conseils des réponses, j'ai pu utiliser la transformation de Hough, choisir des lignes et les filtrer. Mon approche actuelle est plutôt grossière. J'ai supposé que la facture sera toujours inférieure à 15 degrés par rapport à l'image. Je me retrouve avec des résultats raisonnables pour les lignes si c'est le cas (voir ci-dessous). Mais je ne suis pas entièrement 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, donc une certaine forme ou distance par rapport à la ligne Origine est requise. Des idées?

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

90
Nathan Keller

Je suis l'ami de Martin qui y travaillait plus tôt cette année. Ce fut mon tout premier projet de codage, et s'est un peu terminé en un peu précipité, donc le code a besoin de quelques erreurs ... de décodage ... Je vais donner quelques conseils sur 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, passez à eux dès que possible. :RÉ

Au lieu de supprimer les petits objets et/ou le bruit, réduisez les restrictions, afin qu'il accepte plus de bords, puis 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 avoir du mal quand il est sur un morceau de papier blanc, mais fournissait certainement les meilleurs résultats.

Pour la transformation Houghline2(), essayez avec le CV_HOUGH_STANDARD Par opposition au CV_HOUGH_PROBABILISTIC, Cela donnera rho et thêta valeurs, définissant la ligne en coordonnées polaires, puis vous pouvez regrouper les lignes dans une certaine tolérance à celles-ci.

Mon groupement fonctionnait comme une table de recherche, pour chaque ligne produite par la transformation de Hough, cela donnerait une paire rho et thêta. Si ces valeurs étaient à l'intérieur, disons 5% d'une paire de valeurs dans le tableau, elles ont été rejetées, si elles étaient en dehors de ces 5%, une nouvelle entrée a été ajoutée au tableau.

Vous pouvez ensuite analyser beaucoup plus facilement les lignes parallèles ou la distance entre les lignes.

J'espère que cela t'aides.

26
Daniel Crowley

Un groupe d'étudiants de mon université a récemment présenté une application iPhone (et python OpenCV app) 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 (il s'agit d'un texte manuscrit sur du papier blanc avec un assez bon éclairage et peut ne pas fonctionner avec du texte imprimé, cela a très bien fonctionné). La raison en était que cela rend la détection des coins beaucoup plus facile.
  • Transformation de Hough pour les 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). Cela est nécessaire car la Hough Transform n'est pas parfaite car elle fonctionne dans un espace d'échantillonnage discret.
  • Trouvez des paires de lignes qui sont à peu près parallèles et qui coupent d'autres paires pour voir quelles lignes forment des quadruples.

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

17
Martin Foot

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 __== '__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 fonctionne au moins pour tous les échantillons:

1234

16
Vanuan

Au lieu de partir de la détection des bords, vous pouvez utiliser la détection des coins.

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

enter image description here

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

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

result

4
Flayn

Voici le code de @Vanuan en 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

Après la détection des contours, utilisez Hough Transform. Ensuite, placez ces points dans une SVM (machine vectorielle de support) avec leurs étiquettes, si les exemples ont des lignes lisses dessus, SVM n'aura aucune difficulté à diviser les parties nécessaires de l'exemple et d'autres parties. Mon conseil sur SVM, mettez 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
Hephaestus
  1. Ensuite, utilisez des contours ou hough sur l'un des clusters (interne)

  2. Use kmeans segment 2 cluster

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