OpenCV-suppression du bruit dans l'image
j'ai une image ici avec une table.. Dans la colonne de droite, le fond est rempli avec le bruit
Comment détecter les zones de bruit? Je veux seulement appliquer une sorte de filtre sur les pièces avec du bruit parce que j'ai besoin de faire des OCR dessus et n'importe quel type de filtre réduira la reconnaissance globale
Et quel genre de filtre est le meilleur pour supprimer le bruit de fond dans l'image?
comme dit je dois faire OCR sur le image
9 réponses
j'ai essayé quelques filtres / opérations dans OpenCV et il semble que cela fonctionne assez bien.
Étape 1:Dilater l'image
kernel = np.ones((5, 5), np.uint8)
cv2.dilate(img, kernel, iterations = 1)
comme vous le voyez, le bruit est parti mais les personnages sont très légers, donc j'ai Érodé l'image.
Etape 2: Éroder l'image
kernel = np.ones((5, 5), np.uint8)
cv2.erode(img, kernel, iterations = 1)
Comme vous pouvez le voir, le bruit a disparu cependant, certains caractères sur les autres colonnes sont cassés. Je recommande d'exécuter ces opérations sur la colonne bruyante seulement. Vous pouvez utiliser HoughLines pour trouver la dernière colonne. Vous pouvez alors extraire cette colonne seulement, exécuter dilation + érosion et la remplacer par la colonne correspondante dans l'image originale. De plus, dilation + erosion est en fait une opération appelée clôture. Ce que vous pouvez appeler directement en utilisant -
cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
comme @Ermlg l'a suggéré, medianBlur avec un noyau de 3 fonctionne aussi merveilleusement.
cv2.medianBlur(img, 3)
Place
comme vous pouvez le voir tous ces filtres fonctionnent mais il est préférable que vous implémentiez ces filtres seulement dans la partie où le bruit est. Pour ce faire, utilisez la commande suivante:
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)**
alors, vous pouvez extraire cette partie seulement comme :
extract = img[y1:h, x1:w] // w, h are width and height of the image
puis, implémentez le filtre (médian ou fermeture) dans cette image. Après avoir enlevé le bruit, vous devez mettre cette image filtrée à la place de la partie floue dans l'image originale. image[y1: h, x1: w] = median
Ceci est simple en C++:
extract.copyTo(img, new Rect(x1, y1, w - x1, h - y1))
résultat Final avec une autre méthode
ma solution est basée sur le battage pour obtenir l'image résultante en 4 étapes.
- Lire image par
OpenCV 3.2.0
. - Appliquer
GaussianBlur()
pour lisser l'image surtout la région en couleur grise. - masquer l'image pour changer le texte en blanc et le reste en noir.
- inversez l'image masquée en texte noir en blanc.
Le code est Python 2.7
. Il peut être modifié à 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 le images tracées par le code de référence.
Ici image résultat à 2197 x 3218 pixels.
Comme je sais que le filtre médian est la meilleure solution pour réduire le bruit. Je vous recommandons d'utiliser le filtre médian 3x3 fenêtre. Voir la fonction cv:: medianBlur ().
mais soyez prudent lorsque vous utilisez une filtration du bruit en même temps que la ROC. Les Its peuvent entraîner une diminution de la précision de la reconnaissance.
je recommande aussi d'essayer d'utiliser une paire de fonctions (cv::erode() et cv::dilate()). Mais je ne suis pas sûr que la meilleure solution sera alors cv:: medianBlur () avec la fenêtre de 3x3.
je dirais flou médian (probablement 5*5 kernel).
si vous prévoyez d'appliquer ROC l'image. Je vous conseille de le suivant:
- filtrer l'image à L'aide du filtre médian.
- trouver des contours dans l'image filtrée, vous n'obtiendrez que des contours texte (appelez-les F).
- trouver les contours de l'image originale (les appeler O).
- isoler tous les contours dans O qui ont intersection avec tout contour en F.
solution plus rapide:
- trouver les contours de l'image originale.
- Filtrer en fonction de la taille.
si vous êtes très inquiet de supprimer les pixels qui pourraient nuire à votre détection OCR. Sans ajouter d'artefacts, il faut être aussi pur que possible. Ensuite, vous devez créer un filtre blob. Et supprimez tous les blobs qui sont plus petits que n pixels ou ainsi.
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 multithread blobfilter hors de raisons de vitesse). Et désolé mais je ne peux pas partager mon code ici. Simplement décrire comment faire.
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 noirs, et supprimer ceux plus petits que quelques pixels. Il supprimerait tous les points bruyants (à l'exception de ceux touchant une composante VALIDE), mais préserverait tous les caractères et la structure du document (lignes et ainsi de suite).
La fonction à utiliser serait connectedecomponentwithstats (avant que vous ayez probablement besoin de produire l'image négative, le seuil function THRESH_BINARY_INV
fonctionnerait dans ce cas), dessin 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éfini comme les composantes connexes d'une taille minimale et maximale, et l'aspect ratio dans une plage donnée.
j'avais déjà fait face au même problème et obtenu la meilleure solution. Convertir l'image source en niveaux de gris de l'image et appliquer fastNlMeanDenoising fonction et ensuite appliquer seuil.
ce fastNlMeansDenoising(gris,de l'heure d'été,3.0,21,7); seuil (dst,finaldst, 150, 255, THRESH_BINARY);
aussi utiliser peut ajuster le seuil en accordance avec votre image de bruit de fond. par exemple- threshold (dst,finaldst, 200, 255, THRESH_BINARY);
NOTE-Si les lignes de votre colonne ont été supprimées...Vous pouvez prendre un masque de lignes de colonne de l'image source et peut s'appliquer à l'image résultante dénoisée en utilisant des opérations BITWISE comme et, ou, XOR.
essayez de battre l'image comme ceci. Assurez-vous que votre src
est en échelle de gris. Cette méthode ne retiendra que les pixels dont l'intensité se situe entre 150 et 255.
threshold(src, output, 150, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
vous pourriez vouloir Inverser l'image comme vous essayez de nier les pixels gris. Après l'opération, inversez à nouveau pour obtenir le résultat désiré.