web-dev-qa-db-fra.com

Image de nettoyage pour l'OCR

J'ai essayé d'effacer les images pour l'OCR: (les lignes)

enter image description here

J'ai besoin de supprimer ces lignes pour parfois traiter davantage l'image et je m'approche assez, mais la plupart du temps, le seuil enlève trop du texte:

    copy = img.copy()
    blur = cv2.GaussianBlur(copy, (9,9), 0)
    thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,30)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
    dilate = cv2.dilate(thresh, kernel, iterations=2)

    cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]

    for c in cnts:
        area = cv2.contourArea(c)
        if area > 300:
            x,y,w,h = cv2.boundingRect(c)
            cv2.rectangle(copy, (x, y), (x + w, y + h), (36,255,12), 3)

Modifier: De plus, l'utilisation de nombres constants ne fonctionnera pas si la police change. Existe-t-il un moyen générique de procéder?

9
K41F4r

Voici une idée. Nous décomposons ce problème en plusieurs étapes:

  1. Déterminer l'aire de contour rectangulaire moyenne. Nous définissons un seuil puis trouvons les contours et filtrons en utilisant l'aire du rectangle englobant du contour. La raison pour laquelle nous le faisons est à cause de l'observation que tout caractère typique ne sera que si grand alors qu'un grand bruit s'étendra sur une plus grande zone rectangulaire. Nous déterminons ensuite la surface moyenne.

  2. Supprimer les grands contours aberrants. Nous réitérons les contours et supprimons les grands contours s'ils sont 5x plus grande que la surface de contour moyenne en remplissant le contour. Au lieu d'utiliser une zone à seuil fixe, nous utilisons ce seuil dynamique pour plus de robustesse.

  3. Dilatez avec un noyau vertical pour connecter les caractères . L'idée est de profiter de l'observation que les caractères sont alignés en colonnes. En dilatant avec un noyau vertical, nous connectons le texte ensemble afin que le bruit ne soit pas inclus dans ce contour combiné.

  4. Supprimez les petits bruits . Maintenant que le texte à conserver est connecté, nous trouvons les contours et supprimons tous les contours plus petits que 4x l'aire de contour moyenne.

  5. Au niveau du bit et pour reconstruire l'image . Puisque nous avons seulement souhaité que les contours restent sur notre masque, nous les avons au niveau du bit et pour conserver le texte et obtenir notre résultat.


Voici une visualisation du processus:

Nous seuil d'Ots pour obtenir une image binaire puis trouver les contours pour déterminer l'aire de contour rectangulaire moyenne. De là, nous supprimons les grands contours aberrants surlignés en vert par remplissage des contours

enter image description hereenter image description here

Ensuite, nous construisons un noyau vertical et dilate pour connecter les caractères. Cette étape connecte tout le texte souhaité pour conserver et isoler le bruit en blobs individuels.

enter image description here

Maintenant, nous trouvons les contours et filtrons en utilisant zone de contour pour supprimer le petit bruit

enter image description here

Voici toutes les particules de bruit supprimées surlignées en vert

enter image description here

Résultat

enter image description here

Code

import cv2

# Load image, grayscale, and Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Determine average contour area
average_area = [] 
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = w * h
    average_area.append(area)

average = sum(average_area) / len(average_area)

# Remove large lines if contour area is 5x bigger then average contour area
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = w * h
    if area > average * 5:  
        cv2.drawContours(thresh, [c], -1, (0,0,0), -1)

# Dilate with vertical kernel to connect characters
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,5))
dilate = cv2.dilate(thresh, kernel, iterations=3)

# Remove small noise if contour area is smaller than 4x average
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    if area < average * 4:
        cv2.drawContours(dilate, [c], -1, (0,0,0), -1)

# Bitwise mask with input image
result = cv2.bitwise_and(image, image, mask=dilate)
result[dilate==0] = (255,255,255)

cv2.imshow('result', result)
cv2.imshow('dilate', dilate)
cv2.imshow('thresh', thresh)
cv2.waitKey()

Remarque: Le traitement d'image traditionnel se limite au seuillage, aux opérations morphologiques et au filtrage des contours (approximation des contours, zone, rapport d'aspect ou détection de blob). Étant donné que les images d'entrée peuvent varier en fonction de la taille du texte, il est assez difficile de trouver une solution unique. Vous voudrez peut-être étudier la formation de votre propre classificateur avec la machine/l'apprentissage en profondeur pour une solution dynamique.

14
nathancy