web-dev-qa-db-fra.com

Supprimez le bruit de fond de l'image pour rendre le texte plus clair pour l'OCR

J'ai écrit une application qui segmente une image en fonction des régions de texte qu'elle contient et extrait ces régions comme bon me semble. Ce que j'essaie de faire, c'est de nettoyer l'image pour qu'OCR (Tesseract) donne un résultat précis. J'ai l'image suivante comme exemple:

 enter image description here

Exécuter ceci via tesseract donne un résultat largement inexact. Cependant, nettoyez l’image (avec photoshop) pour obtenir l’image comme suit:

 enter image description here

Donne exactement le résultat auquel je m'attendais. La première image est déjà en cours d’exécution avec la méthode suivante pour la nettoyer à ce stade:

 public Mat cleanImage (Mat srcImage) {
    Core.normalize(srcImage, srcImage, 0, 255, Core.NORM_MINMAX);
    Imgproc.threshold(srcImage, srcImage, 0, 255, Imgproc.THRESH_OTSU);
    Imgproc.erode(srcImage, srcImage, new Mat());
    Imgproc.dilate(srcImage, srcImage, new Mat(), new Point(0, 0), 9);
    return srcImage;
}

Que puis-je faire de plus pour nettoyer la première image afin qu'elle ressemble à la deuxième image?

Éditer: Il s’agit de l’image originale avant son exécution par la fonction cleanImage.

 enter image description here

24
Zy0n

Ma réponse est basée sur les hypothèses suivantes. Il est possible qu'aucun d'entre eux ne soit valable dans votre cas.

  • Vous pouvez éventuellement imposer un seuil pour les hauteurs de la boîte limite dans la région segmentée. Ensuite, vous devriez pouvoir filtrer les autres composants.
  • Vous connaissez la largeur de trait moyenne des chiffres. Utilisez ces informations pour minimiser le risque que les chiffres soient connectés à d'autres régions. Vous pouvez utiliser la transformation de distance et les opérations morphologiques pour cela.

Voici ma procédure pour extraire les chiffres:

  • Appliquer le seuil Otsu à l'image  otsu
  • Prenez la transformation à distance  dist
  • Seuil la distance image transformée en utilisant la contrainte de largeur de trait (= 8)  sw2

  • Appliquer une opération morphologique pour déconnecter  ws2op

  • Filtrer les hauteurs du cadre de sélection et deviner où se trouvent les chiffres

largeur de trait = 8  bb largeur de trait = 10  bb2

MODIFIER

  • Préparez un masque en utilisant la coque convexe des contours des chiffres trouvés  mask

  • Copier la région de chiffres dans une image vierge à l'aide du masque

largeur de trait = 8  cl1

largeur de trait = 10  cl2

Ma connaissance de Tesseract est un peu rouillée. Si je me souviens bien, vous pouvez obtenir un niveau de confiance pour les personnages. Vous pourrez peut-être filtrer le bruit à l'aide de ces informations s'il vous reste à détecter des zones bruyantes en tant que cadres de sélection.

Code C++

Mat im = imread("aRh8C.png", 0);
// apply Otsu threshold
Mat bw;
threshold(im, bw, 0, 255, CV_THRESH_BINARY_INV | CV_THRESH_OTSU);
// take the distance transform
Mat dist;
distanceTransform(bw, dist, CV_DIST_L2, CV_DIST_MASK_PRECISE);
Mat dibw;
// threshold the distance transformed image
double SWTHRESH = 8;    // stroke width threshold
threshold(dist, dibw, SWTHRESH/2, 255, CV_THRESH_BINARY);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
// perform opening, in case digits are still connected
Mat morph;
morphologyEx(dibw, morph, CV_MOP_OPEN, kernel);
dibw.convertTo(dibw, CV_8U);
// find contours and filter
Mat cont;
morph.convertTo(cont, CV_8U);

Mat binary;
cvtColor(dibw, binary, CV_GRAY2BGR);

const double HTHRESH = im.rows * .5;    // height threshold
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
vector<Point> digits; // points corresponding to digit contours

findContours(cont, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
{
    Rect rect = boundingRect(contours[idx]);
    if (rect.height > HTHRESH)
    {
        // append the points of this contour to digit points
        digits.insert(digits.end(), contours[idx].begin(), contours[idx].end());

        rectangle(binary, 
            Point(rect.x, rect.y), Point(rect.x + rect.width - 1, rect.y + rect.height - 1),
            Scalar(0, 0, 255), 1);
    }
}

// take the convexhull of the digit contours
vector<Point> digitsHull;
convexHull(digits, digitsHull);
// prepare a mask
vector<vector<Point>> digitsRegion;
digitsRegion.Push_back(digitsHull);
Mat digitsMask = Mat::zeros(im.rows, im.cols, CV_8U);
drawContours(digitsMask, digitsRegion, 0, Scalar(255, 255, 255), -1);
// expand the mask to include any information we lost in earlier morphological opening
morphologyEx(digitsMask, digitsMask, CV_MOP_DILATE, kernel);
// copy the region to get a cleaned image
Mat cleaned = Mat::zeros(im.rows, im.cols, CV_8U);
dibw.copyTo(cleaned, digitsMask);

MODIFIER

Code Java

Mat im = Highgui.imread("aRh8C.png", 0);
// apply Otsu threshold
Mat bw = new Mat(im.size(), CvType.CV_8U);
Imgproc.threshold(im, bw, 0, 255, Imgproc.THRESH_BINARY_INV | Imgproc.THRESH_OTSU);
// take the distance transform
Mat dist = new Mat(im.size(), CvType.CV_32F);
Imgproc.distanceTransform(bw, dist, Imgproc.CV_DIST_L2, Imgproc.CV_DIST_MASK_PRECISE);
// threshold the distance transform
Mat dibw32f = new Mat(im.size(), CvType.CV_32F);
final double SWTHRESH = 8.0;    // stroke width threshold
Imgproc.threshold(dist, dibw32f, SWTHRESH/2.0, 255, Imgproc.THRESH_BINARY);
Mat dibw8u = new Mat(im.size(), CvType.CV_8U);
dibw32f.convertTo(dibw8u, CvType.CV_8U);

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3));
// open to remove connections to stray elements
Mat cont = new Mat(im.size(), CvType.CV_8U);
Imgproc.morphologyEx(dibw8u, cont, Imgproc.MORPH_OPEN, kernel);
// find contours and filter based on bounding-box height
final double HTHRESH = im.rows() * 0.5; // bounding-box height threshold
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
List<Point> digits = new ArrayList<Point>();    // contours of the possible digits
Imgproc.findContours(cont, contours, new Mat(), Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE);
for (int i = 0; i < contours.size(); i++)
{
    if (Imgproc.boundingRect(contours.get(i)).height > HTHRESH)
    {
        // this contour passed the bounding-box height threshold. add it to digits
        digits.addAll(contours.get(i).toList());
    }   
}
// find the convexhull of the digit contours
MatOfInt digitsHullIdx = new MatOfInt();
MatOfPoint hullPoints = new MatOfPoint();
hullPoints.fromList(digits);
Imgproc.convexHull(hullPoints, digitsHullIdx);
// convert hull index to hull points
List<Point> digitsHullPointsList = new ArrayList<Point>();
List<Point> points = hullPoints.toList();
for (Integer i: digitsHullIdx.toList())
{
    digitsHullPointsList.add(points.get(i));
}
MatOfPoint digitsHullPoints = new MatOfPoint();
digitsHullPoints.fromList(digitsHullPointsList);
// create the mask for digits
List<MatOfPoint> digitRegions = new ArrayList<MatOfPoint>();
digitRegions.add(digitsHullPoints);
Mat digitsMask = Mat.zeros(im.size(), CvType.CV_8U);
Imgproc.drawContours(digitsMask, digitRegions, 0, new Scalar(255, 255, 255), -1);
// dilate the mask to capture any info we lost in earlier opening
Imgproc.morphologyEx(digitsMask, digitsMask, Imgproc.MORPH_DILATE, kernel);
// cleaned image ready for OCR
Mat cleaned = Mat.zeros(im.size(), CvType.CV_8U);
dibw8u.copyTo(cleaned, digitsMask);
// feed cleaned to Tesseract
22
dhanushka

Je pense que vous devez travailler davantage sur la partie de prétraitement pour que l'image soit aussi claire que possible avant d'appeler le tesseract.

Quelles sont mes idées à faire qui sont les suivantes:

1- Extraire les contours de l'image et trouver des contours dans l'image (cochez this ) et this

2- Chaque contour a une largeur, une hauteur et une surface, vous pouvez donc filtrer les contours en fonction de la largeur, de la hauteur et de sa surface (cochez this et this ), et vous pouvez utiliser une partie du contour. Code d'analyse ici pour filtrer les contours et plus vous pouvez supprimer les contours qui ne sont pas similaires à un contour "lettre ou chiffre" en utilisant un modèle correspondant aux contours.

3- Après avoir filtré le contour, vérifiez où se trouvent les lettres et les chiffres dans cette image. Vous devrez donc peut-être utiliser des méthodes de détection de texte telles que ici

4- Tout ce dont vous avez besoin maintenant pour supprimer la zone non textuelle et les contours qui ne sont pas bons de l'image

5- Vous pouvez maintenant créer votre méthode de binirisation ou vous pouvez utiliser la méthode tesseract pour effectuer la binirisation vers l'image, puis appeler l'OCR sur l'image.

Bien sûr, ce sont les meilleures étapes pour le faire, vous pouvez en utiliser certaines et cela peut vous suffire.

Autres idées:

  • Vous pouvez utiliser différentes méthodes pour le faire. La meilleure idée est de trouver un moyen de détecter le chiffre et l'emplacement du caractère en utilisant différentes méthodes telles que la correspondance des modèles ou des fonctions telles que HOG.

  • Vous pouvez d’abord réaliser la binarisation de votre image et obtenir l’image binaire. Vous devez ensuite appliquer une ouverture avec une ligne structurelle pour l’horizontale et la verticale, ce qui vous aidera à détecter les contours et à segmenter l’image puis l’OCR .

  • Après avoir détecté tous les contours de l’image, vous pouvez également utiliser Hough transformation pour détecter tout type de ligne et courbe définie comme ceci one , et ainsi détecter les caractères alignés afin de segmenter l’image. et faire l'OCR après cela.

Un moyen beaucoup plus simple:

1- Est-ce que binirization  enter image description here

2- Quelques opérations de morphologie pour séparer les contours:

 enter image description here

3- Inverser la couleur de l'image (cela peut être avant l'étape 2)

 enter image description here

4- Trouver tous les contours dans l'image

 enter image description here

5- Supprimez tous les contours dont la largeur est supérieure à sa hauteur, supprimez les très petits contours, les très grands et les contours non rectangulaires. 

 enter image description here

Remarque: vous pouvez utiliser les méthodes de détection de texte (ou utiliser la détection HOG ou Edge) au lieu des étapes 4 et 5.

6- Trouver le grand rectangle contenant tous les contours restants dans l'image

 enter image description here

7- Vous pouvez faire un prétraitement supplémentaire pour améliorer la saisie du tesseract, puis appeler l’OCR maintenant. (Je vous conseille de rogner l'image et d'en faire une entrée dans l'OCR [je veux dire, rogner le rectangle jaune et ne pas transformer l'image entière en entrée uniquement avec le rectangle jaune et cela améliorera également les résultats])

5
Hazem Abdullah

Cette image vous aiderait-elle?

 enter image description here

L'algorithme produisant cette image serait facile à mettre en œuvre. Je suis sûr que si vous modifiez certains de ses paramètres, vous obtiendrez de très bons résultats pour ce type d'images.

J'ai testé toutes les images avec tesseract:

  • Image d'origine: rien détecté 
  • Image traitée n ° 1: rien détecté 
  • Image traitée n ° 2: 12-14 (correspondance exacte)
  • Mon image traitée: y’1'2-14/j
1
MarkusAtCvlabDotDe

La taille de la police ne doit pas être trop grande ou petite, mais dans une plage de 10 à 12 points (c’est-à-dire que la hauteur des caractères est supérieure à 20 et inférieure à 80). vous pouvez déguster l'image et essayer avec tesseract. Et peu de polices ne sont pas formées à tesseract, le problème peut survenir si ce n’est pas dans ces polices formées. 

0
Gowthaman

Juste un peu de réflexion hors de la boîte:

Je peux voir sur votre image originale qu'il s'agit d'un document préformaté assez rigoureux, qui ressemble à un badge de taxe de circulation ou à quelque chose du genre, n'est-ce pas?

Si l'hypothèse ci-dessus est correcte, vous pouvez alors implémenter une solution moins générique: le bruit que vous essayez de supprimer est dû aux fonctionnalités du modèle de document spécifique, il se produit dans des régions spécifiques et connues de votre image. En fait, le texte aussi.

Dans ce cas, l’un des moyens consiste à définir les limites des régions dans lesquelles vous savez qu’il existe un tel "bruit" et à les effacer.

Suivez ensuite les autres étapes que vous suivez déjà: Appliquez la réduction de bruit qui supprimera les détails les plus fins (c’est-à-dire le motif de fond qui ressemble au filigrane de sécurité ou à l’hologramme du badge). Le résultat devrait être suffisamment clair pour que Tesseract puisse traiter sans problème.

Juste une pensée quand même. Ce n’est pas une solution générique, je le reconnais, cela dépend donc de vos besoins réels.

0
Yannis Douros