J'ai quelques images de pages sur une table. Je voudrais rogner les pages de l'image. Généralement, la page sera le plus grand rectangle de l'image, cependant, les quatre côtés du rectangle peuvent ne pas être visibles dans certains cas.
Je fais ce qui suit mais 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)
Voici quelques exemples:
1er exemple : Je peux trouver le rectangle dans cette image, cependant, je voudrais que la partie restante du bois puisse également être rognée.
2ème exemple : Ne pas trouver les dimensions correctes du rectangle dans cette image.
3e exemple : Impossible de trouver les dimensions correctes sur cette image non plus.
Comme j'ai déjà fait quelque chose de similaire, j'ai expérimenté des transformations de Hough, mais elles étaient beaucoup plus difficiles à corriger pour mon cas que l'utilisation de contours. J'ai les suggestions suivantes pour vous aider à démarrer:
Généralement, le papier (les bords, au moins) est blanc, donc vous pouvez avoir plus de chance en allant dans un espace de couleur 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]
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)
Vous pouvez essayer d'appliquer un détecteur de bord astucieux, plutôt qu'un simple seuil. Pas nécessairement, mais cela peut vous aider:
edges = cv2.Canny(image_blurred,100,300,apertureSize = 3)
Trouvez ensuite les contours. Dans mon cas, je n'ai utilisé que les contours extérieurs extrêmes. Vous pouvez utiliser l'indicateur CHAIN_APPROX_SIMPLE pour compresser le contour
contours,hierarchy = cv2.findContours(edges,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
Maintenant, vous devriez avoir un tas de contours. Il est temps de trouver les bons. Pour chaque contour cnt
, trouvez d'abord la coque convexe, puis utilisez approaxPolyDP
pour simplifier au maximum le contour.
hull = cv2.convexHull(cnt)
simplified_cnt = cv2.approxPolyDP(hull,0.001*cv2.arcLength(hull,True),True)
Maintenant, nous devons utiliser ce contour simplifié pour trouver le quadrilatère englobant. Vous pouvez expérimenter avec de nombreuses règles. La méthode la plus simple consiste à sélectionner les quatre segments les plus longs du contour, puis à créer le quadrilatère englobant en coupant ces quatre lignes. En fonction de votre cas, vous pouvez trouver ces lignes en fonction du contraste qu'elles font, de l'angle qu'elles font et de choses similaires.
Vous avez maintenant un tas de quadrilatères. Vous pouvez maintenant effectuer une méthode en deux étapes pour trouver le quadrilatère requis. Vous supprimez d'abord ceux qui sont probablement faux. Par exemple, un angle du quadrilatère est supérieur à 175 degrés. Ensuite, vous pouvez choisir celui avec la plus grande zone comme résultat final. Vous pouvez voir le contour orange comme l'un des résultats que j'ai obtenus à ce stade:
La dernière étape après avoir trouvé (espérons-le) le quadrilatère droit, se transforme à nouveau en rectangle. Pour cela, vous pouvez utiliser findHomography
pour créer 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 numéros meilleurs et plus intelligents à 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 entraîner quelque chose comme ce qui suit (d'après mes résultats précédents):
J'espère que cela vous aidera à trouver une approche appropriée dans votre cas.
C'est une tâche assez compliquée qui ne peut pas être résolue en recherchant simplement les contours. La couverture de l'économiste, par exemple, ne montre que 1 bord du magazine qui divise l'image en deux. Comment votre ordinateur doit-il savoir lequel est le magazine et lequel est la table? Vous devez donc ajouter beaucoup plus d'intelligence à votre programme.
Vous pouvez rechercher des lignes dans votre image. Transformation de Hough par exemple. Trouvez ensuite des ensembles de lignes plus ou moins parallèles ou orthogonales, des lignes d'une certaine longueur ... Trouvez des impressions en recherchant des couleurs d'impression typiques ou des couleurs que vous ne trouvez généralement pas sur une table. Recherchez des fréquences de contraste élevées créées par des textes imprimés ... Imaginez comment vous, en tant qu'humain, reconnaissez un papier imprimé ...
Dans l'ensemble, c'est une question trop large pour StackOverflow. Essayez de le décomposer en sous-problèmes plus petits, essayez de les résoudre et si vous frappez un mur, revenez ici.