38 votes

Comment recadrer le plus grand rectangle d'une image ?

J'ai quelques images de pages sur un tableau. Je voudrais recadrer les pages dans l'image. En général, la page est le plus grand rectangle de l'image, mais dans certains cas, les quatre côtés du rectangle ne sont pas visibles.

Je fais ce qui suit mais je n'obtiens pas les résultats souhaités :

import cv2
import numpy as np

im = cv2.imread('images/img5.jpg')
gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,127,255,0)
_,contours,_ = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
areas = [cv2.contourArea(c) for c in contours]
max_index = np.argmax(areas)
cnt=contours[max_index]
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow("Show",im)
cv2.imwrite("images/img5_rect.jpg", im)
cv2.waitKey(0)

Vous trouverez ci-dessous quelques exemples :

1er exemple : Je peux trouver le rectangle dans cette image, mais j'aimerais que la partie restante du bois soit également recadrée. enter image description here

enter image description here

2ème exemple : Ne pas trouver les dimensions correctes du rectangle dans cette image. enter image description here

enter image description here

3ème exemple : Impossible de trouver les dimensions correctes dans cette image non plus. enter image description here enter image description here

4ème exemple : Même chose pour ça aussi. enter image description here enter image description here

30voto

Kamyar Infinity Points 2318

Comme j'ai déjà fait quelque chose de similaire, j'ai de l'expérience avec les transformations de Hough, mais elles étaient beaucoup plus difficiles à mettre en place dans mon cas qu'avec les contours. J'ai les suggestions suivantes pour vous aider à démarrer :

  1. En général, le papier (les bords, au moins) est blanc, donc vous aurez peut-être plus de chance en optant pour un espace colorimétrique comme YUV qui sépare mieux la luminosité :

    image_yuv = cv2.cvtColor(image,cv2.COLOR_BGR2YUV)
    image_y = np.zeros(image_yuv.shape[0:2],np.uint8)
    image_y[:,:] = image_yuv[:,:,0]
  2. Le texte sur le papier est un problème. Utilisez un effet de flou, pour (espérons-le) supprimer ces bruits de haute fréquence. Vous pouvez également utiliser des opérations morphologiques comme la dilatation.

    image_blurred = cv2.GaussianBlur(image_y,(3,3),0)
  3. Vous pouvez essayer d'appliquer un détecteur de bords astucieux, plutôt qu'un simple seuil. Pas nécessairement, mais cela peut vous aider :

     edges = cv2.Canny(image_blurred,100,300,apertureSize = 3)
  4. Ensuite, trouvez les contours. Dans mon cas, je n'ai utilisé que les contours extérieurs extrêmes. Vous pouvez utiliser le drapeau CHAIN_APPROX_SIMPLE pour compresser le contour.

    contours,hierarchy = cv2.findContours(edges,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
  5. Maintenant vous devriez avoir un tas de contours. Il est temps de trouver les bons. Pour chaque contour cnt on trouve d'abord la coque convexe, puis on utilise approaxPolyDP pour simplifier le contour autant que possible.

    hull = cv2.convexHull(cnt)
    simplified_cnt = cv2.approxPolyDP(hull,0.001*cv2.arcLength(hull,True),True)
  6. Nous devons maintenant utiliser ce contour simplifié pour trouver le quadrilatère englobant. Vous pouvez expérimenter toutes les règles qui vous viennent à l'esprit. La méthode la plus simple consiste à choisir les quatre segments les plus longs du contour, puis à créer le quadrilatère englobant en croisant ces quatre lignes. Selon votre cas, vous pouvez trouver ces lignes en vous basant sur le contraste de la ligne, l'angle qu'elles font et d'autres choses similaires.

  7. Maintenant tu as un tas de quadrilatères. Vous pouvez maintenant appliquer une méthode en deux étapes pour trouver le quadrilatère dont vous avez besoin. Tout d'abord, vous supprimez ceux qui sont probablement faux. Par exemple, un angle du quadrilatère est supérieur à 175 degrés. Ensuite, vous pouvez choisir celui qui a la plus grande surface comme résultat final. Vous pouvez voir le contour orange comme l'un des résultats que j'ai obtenus à ce stade : All Contours

  8. L'étape finale après avoir trouvé (avec un peu de chance) le bon quadrilatère, est la transformation en un rectangle. Pour cela, vous pouvez utiliser findHomography pour obtenir une matrice de transformation.

    (H,mask) = cv2.findHomography(cnt.astype('single'),np.array([[[0., 0.]],[[2150., 0.]],[[2150., 2800.]],[[0.,2800.]]],dtype=np.single))

    Les chiffres supposent une projection sur du papier à lettres. Vous pouvez trouver des chiffres plus astucieux à utiliser. Vous devez également réorganiser les points de contour pour qu'ils correspondent à l'ordre des coordonnées du papier à lettres. Ensuite, vous appelez warpPerspective pour créer l'image finale :

    final_image = cv2.warpPerspective(image,H,(2150, 2800))

    Cette déformation devrait donner quelque chose comme ce qui suit (d'après mes résultats précédents) : Warping

J'espère que cela vous aidera à trouver une approche appropriée dans votre cas.

10voto

Piglet Points 7662

C'est une tâche assez compliquée qui ne peut être résolue par une simple recherche de contours. La couverture de l'Economist, par exemple, ne montre qu'un seul bord du magazine, ce qui divise l'image en deux. Comment votre ordinateur pourrait-il savoir lequel est le magazine et lequel est la table ? Vous devez donc ajouter beaucoup plus d'intelligence à votre programme.

Vous pourriez rechercher des lignes dans votre image. La transformée de Hough par exemple. Puis trouver des ensembles de lignes plus ou moins parallèles ou orthogonales, des lignes d'une certaine longueur... Trouvez des imprimés en vérifiant les couleurs typiques des imprimés ou les couleurs que vous ne trouvez pas habituellement sur un tableau. Recherchez les fréquences de contraste élevé telles que créées par les textes imprimés... Imaginez comment vous, en tant qu'humain, reconnaissez un papier imprimé...

Dans l'ensemble, cette question est trop vaste pour StackOverflow. Essayez de la décomposer en petits sous-problèmes, essayez de les résoudre et si vous vous heurtez à un mur, revenez ici.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X