IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

OpenCV Discussion :

Corrections de surfaces


Sujet :

OpenCV

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 27
    Points : 19
    Points
    19
    Par défaut Corrections de surfaces
    Bonjour !

    Je suis électronicien mais dans le cadre de mon travail, on m'a assigné un projet informatique pour lequel je manque cruellement d'expérience et de savoir faire.
    Je viens donc, si possible, solliciter votre aide !

    En soit, la tâche va peut être sembler facile pour certains (?) :

    je dois être capable de redresser des surfaces courbées pour les rendre planes en respectant les dimensions.
    Voici un exemple de surface que je suis censé pouvoir redresser : http://static-p3.fotolia.com/jpg/00/...Ld4yZiEmiD.jpg


    Pour faire ce traitement d'image je me suis tourné vers la librairie OpenCV car on trouve beaucoup de documentations dessus.

    Dans un premier temps j'ai voulu faire passer la courbure de la forme comme étant une distorsion de la caméra afin de pouvoir utiliser les fonctions cvCalibrateCamera2 et cvUndistort (Toutes les images étant prises par l'intermédiaire d'une caméra).

    L'idée était donc de projeter un damier sur la surface, d'en faire la capture, et en fonction de la courbure du damier redresser la surface.
    Néanmoins cette méthode ne donne pas grand chose.

    La deuxième idée, et je pense que celle la à plus de chances d'aboutir, est de projeter de simples droites horizontales sur la surface.
    Si la surface est parfaitement plane, les lignes projetées paraîtront donc horizontales et droites et aucun redressement ne sera nécessaire.
    Par contre si la surface est courbée, les lignes projetées seront déformées (en suivant la courbure de la surface) et en fonction de la déformation de ces lignes -> redresser l'image pour les rendre horizontales.
    voici un exemple d'image ou on peut voir l'effet de la courbure d'une surface sur des lignes droites : http://www.adobe.com/fr/designcenter...agewarp_10.jpg

    Néanmoins je n'ai aucune idée de comment implémenter cela sous OpenCV.
    Si quelqu'un à une meilleure idée, un exemple de code source, une référence, ou n'importe quoi d'autre qui puisse m'aiguiller, je suis preneur !

    Je vous remercie.

  2. #2
    Nouveau membre du Club
    Inscrit en
    Février 2011
    Messages
    32
    Détails du profil
    Informations forums :
    Inscription : Février 2011
    Messages : 32
    Points : 39
    Points
    39
    Par défaut
    La deuxième me semble plus prometteuse.
    L'idée est de mettre en correspondance une grille (mesh) constituée de sous-carrés.
    Une fois que vous avez détectés les bords déformés de votre plaque, et que ce bord est modélisé par une courbe (une des diverses splines disponibles : B-spline, nurbs ou autre), vous pouvez considérer (en tout cas dans l'exemple ici) que toutes les mailles de votre mesh sont déformés de la même manière.
    En warpant la spline pour obtenir une droite, vous pouvez alors warper tous les pixels compris dans chaque carré pour obtenir une surface aplanie.

    Si la déformation n'est pas la même sur toute la plaque, le warping sera un peu plus complexe...

    C'est assez visuel, j'espère que cela peut vous aider.

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 27
    Points : 19
    Points
    19
    Par défaut
    Bonjour,

    Tout d'abord merci de votre interêt.
    Je ne suis pas sur d'avoir bien compris la méthode, mais je pense aussi que je me suis mal exprimé :

    Voila où j'en suis

    J'ai une caméra qui me sert notamment à faire des photos d'un livre.
    Un livre ouvert présente une "courbure" que j'aimerais corriger.
    Je projette des lignes horizontales sur le livre, et en fonction de la courbure des lignes (provoquée par la courbure du livre), je veux rectifer la page (la remettre droite).

    Donc je connais la position XY des points des lignes horinzontales quand aucune surface ne se trouve sous ma caméra
    Et je connais la position des points des lignes (incorrectes si courbure) quand la surface est mise sous la caméra.

    Est il possible avec ces 2 tableaux de corriger la deuxieme image pour la remettre droite ?
    En fonction de la différence des coordonées des lignes correctes et "incorrectes", une fonction qui réajuste correctement les pixels sur toute l'image.

    Merci.

  4. #4
    Nouveau membre du Club
    Inscrit en
    Février 2011
    Messages
    32
    Détails du profil
    Informations forums :
    Inscription : Février 2011
    Messages : 32
    Points : 39
    Points
    39
    Par défaut
    J'imagine bien la situation que vous présentez.

    Par rapport à mon exemple initial, la projection entre les lignes lumineuses "à plat" et les lignes lumineuses "courbées" n'est pas une homographie.

    La technique qui va remettre à plat votre image reste le warping.

    Pour la mettre en application, vous devez construire une grille à l'aide de vos lignes lumineuses.

    Sur l'image d'origine, vous allez avoir une série de lignes perpendiculaires aux lignes lumineuses, pour former une grille (de ce genre http://www.retinamd.com/images/NormalGrid.jpg).

    Ces mêmes lignes rajoutées sur la photo avec vos lignes lumineuses courbes donne une grille "déformée".

    Si vous connaissez les coordonnées des points de la grille de l'image d'origine et les coordonnées des points de la grille déformée, vous pouvez appliquer un warping entre ces deux ensembles de points.

    Vous pouvez trouver ici du code pour réaliser un morphing : http://davis.wpi.edu/~matt/courses/morph/2d.htm
    L'idée est de récupérer la partie warping du code, par exemple.
    JAI dispose également d'une classe Warp, qui peut être adaptée : http://download.oracle.com/docs/cd/E.../jai/Warp.html
    Un autre exmple en AS : http://www.mukimuki.fr/flashblog/2009/05/10/morphing/

  5. #5
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 27
    Points : 19
    Points
    19
    Par défaut
    En effet, ça a l'air d'être exactement ce que je recherche !

    Je me mets au travail dès demain pour voir ce que ça donne.

    Par contre, étant donné la méthode, il ne serait pas encore plus simple alors de projeter directement un quadrillage sur la surface ?
    D'en récupérer toutes les intersections (possible avec openCV il me semble) et de réajuster les intersections qui ne se trouvent pas la ou elles doivent être (via le "mesh warping") ?

    J'ai la possibilité de projeter n'importe quelle forme sur la surface à corriger donc ..

    En tous cas je vous remercie et vous tiendrai au courant !

  6. #6
    Nouveau membre du Club
    Inscrit en
    Février 2011
    Messages
    32
    Détails du profil
    Informations forums :
    Inscription : Février 2011
    Messages : 32
    Points : 39
    Points
    39
    Par défaut
    Si vous pouvez projeter directement une grille sur vos pages, cela peut effectivement aller encore plus vite. Mais si vous disposez déjà d'images qui n'ont pas profité de la grille, mais seulement de ligne selon un seul axe, ce n'est pas "très" compliqué d'en rajouter "virtuellement".

    A noter que tous les warping réalisés en suivant une triangulation (type delaunay) seront moins précis qu'un warping utilisant une spline. Une spline va reporter les modifications sur un nœud sur plus de points qu'un delaunay.

    IL y a des cas où il est désirable qu'un nœud de la grille qui se déplace n'influence que localement ses voisins (et ne reporte donc pas ses modifications sur l'ensemble de la grille des points), mais dans votre cas précis, la distorsion originale se fait sur une page de livre. Et à moins que votre livre ne soit édité sur du papier de taille A0 (ou sur une feuille de papier bible), une petite torsion ou déformation de la page où qu'elle soit va influencer la géométrie de l'ensemble de la page.

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 27
    Points : 19
    Points
    19
    Par défaut Résolu
    J'ai finalement réussi à résoudre mon problème avec des simples transformations warp (je n'ai pas du aller jusqu'au mesh-warping) et sans devoir utiliser de projections !

    J'utilise tout simplement le bords des surfaces qui sont représentatifs de la déformation et je warp en fonction de ces derniers.

    Merci pour votre aide !

  8. #8
    Nouveau membre du Club
    Inscrit en
    Février 2011
    Messages
    32
    Détails du profil
    Informations forums :
    Inscription : Février 2011
    Messages : 32
    Points : 39
    Points
    39
    Par défaut
    Content que ça ai marché. Vous avez une url ou autre sur laquelle on pourrait voir le résultat final, au cas où ? (c'est toujours intéressant...)

  9. #9
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 27
    Points : 19
    Points
    19
    Par défaut
    Voici un lien avec le résultat obtenu :

    http://nip.dev.isib.be/files/2011/05/CompareResult.jpg

    Le résultat est convaincant même s'il n'est pas parfait (la qualité de l'image d'origine n'est probablement pas suffisante).

  10. #10
    Membre confirmé
    Inscrit en
    Mars 2010
    Messages
    439
    Détails du profil
    Informations forums :
    Inscription : Mars 2010
    Messages : 439
    Points : 533
    Points
    533
    Par défaut
    ce n'est pas mal du tout, tu devrais poster ton code si possible pour aider ceux que ca intéresse, tu pourrais jouer sur les ombres peut être pour affiner, le souci ici c'est que tu as une image très plane donc forcèment la perspective ne rend pas énormément

  11. #11
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 27
    Points : 19
    Points
    19
    Par défaut
    Oui, j'ai pensé homogénéiser les effets de lumières mais je ne sais pas trop par ou m'y prendre pour que le traitement soit automatique.

    Je viendrai poster le code la semaine prochaine. Je dois encore effectuer différents tests pour l'améliorer d'ici là.

  12. #12
    Membre confirmé
    Inscrit en
    Mars 2010
    Messages
    439
    Détails du profil
    Informations forums :
    Inscription : Mars 2010
    Messages : 439
    Points : 533
    Points
    533
    Par défaut
    un petit flou devrait aider si ce n'est déjà fait, mais vraiment léger le flou sinon tu perds les informations utiles, tu peux aussi faire un seuillage pour en éliminer une partie.

  13. #13
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 27
    Points : 19
    Points
    19
    Par défaut Code
    Voici une partie du code de l'algorithme. Je suis ouvert (et démandeur) à toutes critiques pour améliorer cet algorithme et le rendre robuste.

    Pour la première partie, la plus importante, je ne mettrai pas les lignes de codes que j'utilise.
    Tout simplement car cette partie est encore très floue et mon code ne fonctionne que dans peu de circonstances.

    1) Détection des contours et des 4 coins

    Le plus important pour cet algorithme est la détection des contours.
    Pour se faire j'utilise, pour le moment, le filtre de Laplace.

    Néanmoins ce filtre ne donne pas toujours tous les pixels. Il faudrait donc arriver à faire une détection intelligente des contours (extrapolation de pixels quand ceux ci ne sont pas trouvés ou quelque chose dans le genre).
    De plus, quand une page est rognée, il serait intéressant de pouvoir le détecter et, de nouveau, extrapoler le bout de page manquant.

    Je n'ai pas encore trouvé de solution pour cette étape (ce qui explique pourquoi mon code ne fonctionne pas dans toutes les situations).

    Une fois les bords trouvés (tous les pixels, il ne faut pas de "trous" !!!), il faut trouver les 4 coins des surfaces.
    Ici, j'utilise la fonction cvGoodFeatureToTrack mais, comme l'etape précédente, cette fonction ne donne pas toujours le résultat voulu.

    Dans un futur proche, je pense regarder de plus près la fonction cvFindContours. Avec cette fonction, il me semble qu'il est plus facile de déterminer plus précisément les contours ainsi que les 4 coins de ces contours.

    Si vous avez d'autres pistes ...

    2) Premier Warping

    Une fois les 4 coins trouvés, j'effectue un premier warp sur l'image. Ce warp va servir à remettre les 4 coins aux mêmes ordonnées et abscisses afin de redonner un aspect rectangulaire à la page.

    Les 4 coins se trouve dans un tableau de CvPoint nommé srcQuad[].

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    srcQuad[0].x<srcQuad[2].x?dstQuad[0].x=srcQuad[0].x:dstQuad[0].x=srcQuad[2].x;
    srcQuad[0].y<srcQuad[1].y?dstQuad[0].y=srcQuad[0].y:dstQuad[0].y=srcQuad[1].y;
     
    srcQuad[1].x>srcQuad[3].x?dstQuad[1].x=srcQuad[1].x:dstQuad[1].x=srcQuad[3].x;
    dstQuad[1].y=dstQuad[0].y;
     
    dstQuad[2].x=dstQuad[0].x;
    srcQuad[2].y>srcQuad[3].y?dstQuad[2].y=srcQuad[2].y:dstQuad[2].y=srcQuad[3].y;
     
    dstQuad[3].x=dstQuad[1].x;
    dstQuad[3].y=dstQuad[2].y;
     
    CvMat *warp_matrix = cvCreateMat(3,3,CV_32FC1);
    cvGetPerspectiveTransform(srcQuad,dstQuad,warp_matrix);
    cvWarpPerspective(img,imgwarp,warp_matrix);
    3) Récupération des contours

    Dans mon cas, avec le filtre de Laplace, je parcours l'image afin de récupérer les contours (c'est pourquoi il ne faut pas de "trous" lors de la détection).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    /*Aller rechercher les pixels des bords horizontaux*/
    CvMat* haut(cvCreateMat((int)((dstQuad[1].x-dstQuad[0].x)),2,CV_32FC1));
    CvMat* bas(cvCreateMat((int)((dstQuad[1].x-dstQuad[0].x)),2,CV_32FC1));
     
    //les 2 premiers points (haut gauche et bas gauche) sont connus
    cvmSet(haut,0,0,dstQuad[0].x);
    cvmSet(haut,0,1,dstQuad[0].y);
    cvmSet(bas,0,0,dstQuad[2].x);
    cvmSet(bas,0,1,dstQuad[2].y);
     
    i=1;//premier point deja connu
    for(int x=(int)dstQuad[0].x+1;x<dstQuad[1].x;i++,x++){
    	y=0;
    	while(((CvScalar)cvGet2D(imgwarp,y,x)).val[0]<200)y++;
    	cvmSet(haut,i,0,x);
    	cvmSet(haut,i,1,y);
    }
     
    i=1;
    for(int x=(int)dstQuad[0].x+1;x<dstQuad[1].x;i++,x++){
    	y=imgwarp->height-1;
    	while(((CvScalar)cvGet2D(imgwarp,y,x)).val[0]<200)y--;
    	cvmSet(bas,i,0,x);
    	cvmSet(bas,i,1,y);
    }
    L'utilisation de la fonction cvFindContours devrait pouvoir remplacer cette étape (elle renvoie déjà les contours me semble-t-il)

    4) Warp final

    Quand les pixels des contours sont connus, il suffit d'appliquer une série de warp sur l'image.
    J'utilise des zones d’intérêts avec un dw de 2 pixels et, pour toutes ces zones d’intérêts, le warp consiste à repositionner les pixels des bords sur une référence choisie (dans cet exemple j'ai pris le sommet des contours).
    Ainsi, tous les points se retrouveront aux mêmes ordonnées et la page récupère sa forme rectangulaire.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    int x(0),y(0),dw(2),dh(0);
     
    //trouver le y le plus petit
    y=imgwarp->height;
    for(int j=0;j<haut->rows-1;j++){
    	if(y>cvmGet(haut,j,1))y=(int)cvmGet(haut,j,1);
    }
     
    //trouver le y le plus grand
    for(int j=0;j<bas->rows-1;j++){
    	if(dh<cvmGet(bas,j,1))dh=(int)cvmGet(bas,j,1);
    }
    	/*Warp de tous les rectangles*/
    for(i=0;i<haut->rows-1;i+=2){
    	//les x sont les mêmes
    	x=(int)cvmGet(haut,i,0);	
    	CvRect ROI=cvRect(x,0,dw,imgwarp->height); //roi sur toute la hauteur
    	cvSetImageROI(imgwarp,ROI);
    	cvSetImageROI(resultat,ROI);
     
    	//pnt src (contours)
    	srcQuad[0].x=0;
    	srcQuad[0].y=cvmGet(haut,i,1);
    	srcQuad[1].x=1;
    	srcQuad[1].y=cvmGet(haut,i+1,1);
    	srcQuad[2].x=0;
    	srcQuad[2].y=cvmGet(bas,i,1);
    	srcQuad[3].x=1;
    	srcQuad[3].y=cvmGet(bas,i+1,1);
     
    	//on remet tout sur la référence
    	dstQuad[0].x=0;
    	dstQuad[0].y=y;
    	dstQuad[1].x=1;
    	dstQuad[1].y=y;
    	dstQuad[2].x=0;
    	dstQuad[2].y=dh;
    	dstQuad[3].x=1;
    	dstQuad[3].y=dh;
     
    	warp_matrix = cvCreateMat(3,3,CV_32FC1);
    	cvGetPerspectiveTransform(srcQuad,dstQuad,warp_matrix);
    	cvWarpPerspective( imgwarp, resultat, warp_matrix);
    	cvResetImageROI(imgwarp);
    	cvResetImageROI(resultat);
    }

  14. #14
    Nouveau membre du Club
    Inscrit en
    Février 2011
    Messages
    32
    Détails du profil
    Informations forums :
    Inscription : Février 2011
    Messages : 32
    Points : 39
    Points
    39
    Par défaut
    Joli travail !

    Pour "améliorer" l'image finale, cvEqualizeHist() permet d'étaler l'histogramme de couleurs, sur une image noir et blanc.
    L'idéal serait de coder une vraie balance des blancs, mais ça peut déjà donner des résultats intéressants.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Taille des surfaces avec DirectDraw
    Par Shakram dans le forum DirectX
    Réponses: 5
    Dernier message: 09/09/2002, 00h42
  2. Effet Fade In / Fade Out sur une surface DirectDraw
    Par Magus (Dave) dans le forum DirectX
    Réponses: 3
    Dernier message: 08/09/2002, 17h37
  3. Sauvegarder une surface dans un fichier
    Par Freakazoid dans le forum DirectX
    Réponses: 6
    Dernier message: 18/08/2002, 15h23
  4. Redimensionnement d'une surface
    Par Freakazoid dans le forum DirectX
    Réponses: 4
    Dernier message: 01/07/2002, 22h01
  5. Opengl -- Les surfaces
    Par Anonymous dans le forum OpenGL
    Réponses: 2
    Dernier message: 02/05/2002, 10h14

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo