web-dev-qa-db-fra.com

OpenCV - Suppression du bruit dans l'image

J'ai une image ici avec un tableau .. Dans la colonne de droite le fond est rempli de bruit

Comment détecter les zones bruyantes? Je veux seulement appliquer une sorte de filtre sur les parties avec du bruit parce que je dois faire de l'OCR dessus et tout type de filtre réduira la reconnaissance globale.

Et quel type de filtre est le meilleur pour supprimer le bruit de fond dans l'image?

Comme je l'ai dit, je dois faire de l'OCR sur l'image

 enter image description here

18
clarkk

J'ai essayé quelques filtres/opérations dans OpenCV et cela semble plutôt bien fonctionner.

Étape 1: Dilater l'image - 

kernel = np.ones((5, 5), np.uint8)
cv2.dilate(img, kernel, iterations = 1)

 Dilated Image

Comme vous le voyez, le bruit est parti mais les caractères sont très légers, alors j'ai érodé l'image. 

Étape 2: Erode l'image - 

kernel = np.ones((5, 5), np.uint8)
cv2.erode(img, kernel, iterations = 1)

 Eroded dilated image

Comme vous pouvez le constater, le bruit est parti, mais certains caractères des autres colonnes sont brisés. Je recommanderais d'exécuter ces opérations uniquement sur la colonne bruyante. Vous voudrez peut-être utiliser HoughLines pour trouver la dernière colonne. Ensuite, vous pouvez extraire uniquement cette colonne, exécuter dilatation + érosion et la remplacer par la colonne correspondante dans l'image d'origine. De plus, dilatation + érosion est en fait une opération appelée fermeture. Vous pouvez appeler cela directement en utilisant - 

cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

Comme @Ermlg l'a suggéré, medianBlur avec un noyau de 3 fonctionne également à merveille.

cv2.medianBlur(img, 3)

 Median Blur

Étape alternative

Comme vous pouvez le constater, tous ces filtres fonctionnent, mais il est préférable de ne les implémenter que dans la partie où se trouve le bruit. Pour ce faire, utilisez ce qui suit:

edges = cv2.Canny(img, 50, 150, apertureSize = 3) // img is gray here
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, 1000, 50) // last two arguments are minimum line length and max gap between two lines respectively.
for line in lines: 
    for x1, y1, x2, y2 in line: 
        print x1, y1
// This gives the start coordinates for all the lines. You should take the x value which is between (0.75 * w, w) where w is the width of the entire image. This will give you essentially **(x1, y1) = (1896, 766)**

Ensuite, vous pouvez extraire cette partie uniquement comme:

extract = img[y1:h, x1:w] // w, h are width and height of the image

 Extracted image

Ensuite, implémentez le filtre (médiane ou fermeture) dans cette image. Après avoir supprimé le bruit, vous devez mettre cette image filtrée à la place de la partie floue de l’image originale. image [y1: h, x1: w] = médiane

C'est simple en C++: 

extract.copyTo(img, new Rect(x1, y1, w - x1, h - y1))

Résultat final avec une autre méthode

 Final Result J'espère que ça aide!

9
Rick M.

Ma solution est basée sur le seuillage pour obtenir l'image résultante en 4 étapes.

  1. Lire l'image par OpenCV 3.2.0
  2. Appliquez GaussianBlur() pour lisser l’image, en particulier la région grisée. 
  3. Masquez l'image pour changer le texte en blanc et le reste en noir. 
  4. Inverser l'image masquée en texte noir en blanc.

Le code est dans Python 2.7. Il peut être changé en C++ facilement.

import numpy as np
import cv2
import matplotlib.pyplot as plt
%matplotlib inline 

# read Danish doc image 
img = cv2.imread('./imagesStackoverflow/danish_invoice.png')

# apply GaussianBlur to smooth image
blur = cv2.GaussianBlur(img,(5,3), 1) 

# threshhold gray region to white (255,255, 255) and sets the rest to black(0,0,0)
mask=cv2.inRange(blur,(0,0,0),(150,150,150))

# invert the image to have text black-in-white
res = 255 - mask

plt.figure(1)
plt.subplot(121), plt.imshow(img[:,:,::-1]), plt.title('original') 
plt.subplot(122), plt.imshow(blur, cmap='gray'), plt.title('blurred')
plt.figure(2)
plt.subplot(121), plt.imshow(mask, cmap='gray'), plt.title('masked')
plt.subplot(122), plt.imshow(res, cmap='gray'), plt.title('result')
plt.show()

Ce qui suit est les images tracées par le code pour référence.

 enter image description here

Voici l'image résultat à 2197 x 3218 pixels.

 enter image description here

7
thewaywewere

Comme je le sais, le filtre médian est la meilleure solution pour réduire le bruit. Je recommanderais d'utiliser un filtre médian avec une fenêtre 3x3. Voir fonction cv :: medianBlur ()

Mais soyez prudent lorsque vous utilisez une filtration de bruit simultanément avec OCR. Cela peut entraîner une diminution de la précision de la reconnaissance.

Aussi, je recommanderais d'essayer d'utiliser une paire de fonctions (cv :: erode () et cv :: dilate ()). Mais je ne suis pas sûr que ce sera la meilleure solution alors cv :: medianBlur () avec la fenêtre 3x3.

3
ErmIg

J'irais avec le flou médian (probablement 5 * 5 noyau). 

si vous envisagez d'appliquer l'OCR à l'image. Je vous conseillerais ce qui suit:

  1. Filtrer l'image à l'aide du filtre médian.
  2. Recherchez des contours dans l’image filtrée, vous n’obtiendrez que des contours de texte (appelez-lesF).
  3. Recherchez des contours dans l'image d'origine (appelez-lesO).
  4. isoler tous les contours dansOqui ont une intersection avec tout contour dansF.

Solution plus rapide:

  1. Trouver des contours dans l'image d'origine.
  2. Filtrez-les en fonction de la taille.
2
Humam Helfawi

Résultat:

 enter image description here

2
Antonio

Si le temps de traitement n’est pas un problème, une méthode très efficace dans ce cas serait de calculer tous les composants connectés en noir et de supprimer ceux dont la taille est inférieure à quelques pixels. Cela supprimerait tous les points bruyants (sauf ceux touchant un composant valide), mais conserverait tous les caractères et la structure du document (lignes, etc.).

La fonction à utiliser serait connectedComponentWithStats (avant que vous deviez probablement produire l'image négative, la fonction threshold avec THRESH_BINARY_INV fonctionnerait dans ce cas), en dessinant des rectangles blancs où de petits composants connectés ont été trouvés.

En fait, cette méthode pourrait être utilisée pour rechercher des caractères, définis en tant que composants liés d’une taille minimale et maximale donnée, et ayant un rapport hauteur/largeur compris dans une plage donnée.

1
Antonio

J'avais déjà fait face au même problème et obtenu la meilleure solution… .. Convertir l'image source en image en niveaux de gris et appliquer la fonction fastNlMeanDenoising, puis appliquer seuil.

Comme ceci - fastNlMeansDenoising (grey, dst, 3.0,21,7); seuil (dst, finaldst, 150,255, THRESH_BINARY);

ALSO use peut ajuster le seuil en fonction de l’image de bruit de fond. eg- seuil (dst, finaldst, 200,255, THRESH_BINARY);

REMARQUE - Si vos lignes de colonne ont été supprimées ... Vous pouvez utiliser un masque de lignes de colonne de l'image source et l'appliquer à l'image obtenue avec bruit en utilisant des opérations BITWISE telles que AND, OR, XOR.

1
Amul Mittal

Si vous êtes très inquiet de supprimer les pixels qui pourraient nuire à votre détection OCR. Sans ajouter des artefacts, soyez le plus pur possible. Ensuite, vous devriez créer un filtre blob. Et supprimez toutes les taches qui sont plus petites que n pixels ou plus. 

Je ne vais pas écrire de code, mais je sais que cela fonctionne très bien car je l'utilise moi-même, bien que je n'utilise pas openCV (j'ai écrit mon propre filtre blob multithread pour des raisons de vitesse). Et désolé mais je ne peux pas partager mon code ici. Juste en décrivant comment le faire.

1
user3800527