2 votes

Comment trouver le contour d'une image scannée d'un formulaire complété ?

Je voudrais détecter le contour du formulaire complété dans ce scan.

Idéalement, je voudrais trouver les coins de la table peinte en rouge.

Mon objectif final est de détecter que l'ensemble du document a été scanné et que les quatre coins sont à l'intérieur des limites du scan.

J'ai utilisé OpenCV à partir de Python - mais il n'a pas été capable de trouver le contour du grand conteneur.

Des idées?

1voto

nathancy Points 6407

Avec l'observation que le formulaire peut être identifié en utilisant la grille de table, voici une approche simple :

  1. Obtenir une image binaire. Chargez l'image, en niveaux de gris, flou gaussien, puis seuillage d'Otsu pour obtenir une image binaire

  2. 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

  3. 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

  4. 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.

  5. 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)

0voto

Yves Daoust Points 6396

Que diriez-vous d'utiliser la transformation de Hough avec une plage de direction étroite, pour trouver les verticales et horizontales ? Si vous avez de la chance, celles dont vous avez besoin seront les plus longues, et après les avoir sélectionnées, vous pourrez reconstruire le rectangle.

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