Avec l'observation que le formulaire peut être identifié en utilisant la grille de table, voici une approche simple :
-
Obtenir une image binaire. Chargez l'image, en niveaux de gris, flou gaussien, puis seuillage d'Otsu pour obtenir une image binaire
-
Trouver les sections horizontales. Nous créons un noyau en forme horizontale et trouvons les lignes de table horizontales ainsi que les dessinons sur un masque
-
Trouver les sections verticales. Nous créons un noyau en forme verticale et trouvons les lignes de table verticales ainsi que les dessinons sur un masque
-
Remplir le corps du document texte et morph ouvrir. Nous effectuons des opérations morphologiques pour fermer la table, puis trouver les contours et remplir le masque pour obtenir un contour de la forme. Cette étape répond à vos besoins car vous pouvez simplement trouver des contours sur le masque, mais nous pouvons aller plus loin et extraire seulement les sections désirées.
-
Effectuer une transformation de perspective à quatre points. Nous trouvons les contours, les trions pour le plus grand contour, les trions en utilisant une approximation de contour puis effectuons une transformation de perspective à quatre points pour obtenir une vue en plongée de l'image.
Voici les résultats :
Image d'entrée
Contour détecté à extraire surligné en vert
Sortie après la transformation de perspective à 4 points
Code
import cv2
import numpy as np
from imutils.perspective import four_point_transform
# Charger l'image, créer un masque, en niveaux de gris et faire un seuillage Otsu
image = cv2.imread('1.jpg')
mask = np.zeros(image.shape, dtype=np.uint8)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,3)
# Trouver les sections horizontales et dessiner sur le masque
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (80,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(mask, [c], -1, (255,255,255), -1)
# Trouver les sections verticales et dessiner sur le masque
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,50))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(mask, [c], -1, (255,255,255), -1)
# Remplir le corps du document texte
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
close = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, close_kernel, iterations=3)
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(mask, [c], -1, 255, -1)
# Effectuer des opérations morphologiques pour enlever le bruit
# Trouver les contours et trier pour le plus grand contour
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, close_kernel, iterations=5)
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
displayCnt = None
for c in cnts:
# Effectuer une approximation du contour
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
if len(approx) == 4:
displayCnt = approx
break
# Obtenir une vue en plongée de l'image
warped = four_point_transform(image, displayCnt.reshape(4, 2))
cv2.imwrite('mask.png', mask)
cv2.imwrite('thresh.png', thresh)
cv2.imwrite('warped.png', warped)
cv2.imwrite('opening.png', opening)