Voici une idée. Nous décomposons ce problème en plusieurs étapes:
Déterminer l'aire de contour rectangulaire moyenne. Nous seuil puis trouver les contours et filtrer en utilisant la zone rectangle englobante 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 que le grand bruit s'étendra sur une plus grande zone rectangulaire. Nous déterminons ensuite la surface moyenne.
Supprimez les grands contours aberrants. Nous réitérons les contours et supprimons les grands contours s'ils sont 5x
plus grands que la zone de contour moyenne en remplissant le contour. Au lieu d'utiliser une zone à seuil fixe, nous utilisons ce seuil dynamique pour plus de robustesse.
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é.
Supprimez les petits bruits . Maintenant que le texte à conserver est connecté, nous trouvons les contours et supprimons tous les contours plus petits que 4x
la zone de contour moyenne.
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 avons le seuil d' Otsu pour obtenir une image binaire puis trouver des contours pour déterminer l'aire de contour rectangulaire moyenne. De là, nous supprimons les grands contours aberrants surlignés en vert en remplissant les contours
Ensuite, nous construisons un noyau vertical et dilatons pour connecter les caractères. Cette étape connecte tout le texte souhaité pour conserver et isoler le bruit en blobs individuels.
Maintenant, nous trouvons les contours et filtrons en utilisant la zone de contour pour éliminer le petit bruit
Voici toutes les particules de bruit supprimées surlignées en vert
Résultat
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 taches). É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 singulière. Vous voudrez peut-être étudier la formation de votre propre classificateur avec machine / deep learning pour une solution dynamique.