web-dev-qa-db-fra.com

Seuillage d'image rapide

Quel est un moyen rapide et fiable de seuiller des images avec un flou possible et une luminosité non uniforme?

Exemple (flou mais luminosité uniforme):

enter image description here

Parce que l'image n'est pas garantie d'avoir une luminosité uniforme, il n'est pas possible d'utiliser un seuil fixe. Un seuil adaptatif fonctionne bien, mais en raison du flou, il crée des ruptures et des distorsions dans les fonctionnalités (ici, les fonctionnalités importantes sont les chiffres de Sudoku):

enter image description here

J'ai également essayé d'utiliser l'égalisation d'histogramme (en utilisant la fonction equalizeHist d'OpenCV). Il augmente le contraste sans réduire les différences de luminosité.

La meilleure solution que j'ai trouvée est de diviser l'image par sa fermeture morphologique (crédit à ce post ) pour uniformiser la luminosité, puis renormaliser, puis utiliser un seuil fixe (en utilisant l'algorithme d'Otsu pour choisir le niveau de seuil optimal):

enter image description here

Voici le code pour cela dans OpenCV pour Android:

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(19,19));
Mat closed = new Mat(); // closed will have type CV_32F
Imgproc.morphologyEx(image, closed, Imgproc.MORPH_CLOSE, kernel);
Core.divide(image, closed, closed, 1, CvType.CV_32F);
Core.normalize(closed, image, 0, 255, Core.NORM_MINMAX, CvType.CV_8U);
Imgproc.threshold(image, image, -1, 255, Imgproc.THRESH_BINARY_INV
    +Imgproc.THRESH_OTSU); 

Cela fonctionne très bien mais l'opération de fermeture est très lente. La réduction de la taille de l'élément structurant augmente la vitesse mais réduit la précision.

Edit: basé sur la suggestion de DCS, j'ai essayé d'utiliser un filtre passe-haut. J'ai choisi le filtre laplacien, mais je m'attendrais à des résultats similaires avec les filtres Sobel et Scharr. Le filtre capte le bruit haute fréquence dans les zones qui ne contiennent pas de caractéristiques et souffre d'une distorsion similaire au seuil adaptatif en raison du flou. cela prend aussi environ aussi longtemps que l'opération de fermeture. Voici un exemple avec un filtre 15x15:

enter image description here

Edit 2: Sur la base de la réponse d'AruniRC, j'ai utilisé la détection Canny Edge sur l'image avec les paramètres suggérés:

double mean = Core.mean(image).val[0];
Imgproc.Canny(image, image, 0.66*mean, 1.33*mean);

Je ne sais pas comment fiable automatiquement affiner les paramètres pour obtenir les chiffres connectés.

enter image description here

30
1''

En utilisant les suggestions de Vaughn Cato et Theraot, j'ai réduit l'image avant de la fermer, puis j'ai redimensionné l'image fermée jusqu'à sa taille normale. J'ai également réduit la taille du noyau proportionnellement.

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(5,5));
Mat temp = new Mat(); 

Imgproc.resize(image, temp, new Size(image.cols()/4, image.rows()/4));
Imgproc.morphologyEx(temp, temp, Imgproc.MORPH_CLOSE, kernel);
Imgproc.resize(temp, temp, new Size(image.cols(), image.rows()));

Core.divide(image, temp, temp, 1, CvType.CV_32F); // temp will now have type CV_32F
Core.normalize(temp, image, 0, 255, Core.NORM_MINMAX, CvType.CV_8U);

Imgproc.threshold(image, image, -1, 255, 
    Imgproc.THRESH_BINARY_INV+Imgproc.THRESH_OTSU);

L'image ci-dessous montre les résultats côte à côte pour 3 méthodes différentes:

Gauche - fermeture de taille normale (432 pixels), noyau de taille 19

Moyen - fermeture demi-taille (216 pixels), noyau de taille 9

Droite - fermeture quart de taille (108 pixels), noyau de taille 5

enter image description here

La qualité de l'image se détériore à mesure que la taille de l'image utilisée pour la fermeture diminue, mais la détérioration n'est pas suffisamment importante pour affecter les algorithmes de reconnaissance des fonctionnalités. La vitesse augmente légèrement plus de 16 fois pour la fermeture quart de taille, même avec le redimensionnement, ce qui suggère que le temps de fermeture est à peu près proportionnel au nombre de pixels dans l'image.

Toutes les suggestions sur la façon d'améliorer encore cette idée (soit en réduisant davantage la vitesse, soit en réduisant la détérioration de la qualité de l'image) sont les bienvenues.

20
1''

Nous utilisons l'algorithme Bradleys pour un problème très similaire (pour segmenter les lettres de l'arrière-plan, avec une lumière inégale et une couleur d'arrière-plan inégale), décrit ici: http://people.scs.carleton.ca:8008/~roth/iit- publications-iti/docs/gerh-50002.pdf , code C # ici: http://code.google.com/p/aforge/source/browse/trunk/Sources/Imaging/Filters/Adaptive + Binarisation/BradleyLocalThresholding.cs? R = 136 . Il fonctionne sur l'image intégrale, qui peut être calculée en utilisant la fonction integral d'OpenCV. Il est très fiable et rapide, mais lui-même n'est pas implémenté dans OpenCV, mais il est facile à porter.

Une autre option est la méthode adaptiveThreshold dans openCV, mais nous n'avons pas essayé: http://docs.opencv.org/modules/imgproc/doc/miscundry_transformations.html#adaptivethreshold . La version MEAN est la même que les bradleys, sauf qu'elle utilise une constante pour modifier la valeur moyenne au lieu d'un pourcentage, ce qui je pense est mieux.

En outre, un bon article est ici: https://dsp.stackexchange.com/a/2504

2
Oliv

Approche alternative:

En supposant que votre intention est d'avoir les chiffres clairement binarisés ... déplacez votre focus sur les composants plutôt que sur l'image entière.

Voici une approche assez simple:

  1. Faites un edgemap Canny sur l'image. Essayez-le d'abord avec les paramètres de la fonction Canny dans la plage du seuil bas à 0,66 * [valeur moyenne] et du seuil haut à 1,33 * [valeur moyenne]. (signifiant la moyenne des valeurs de niveau de gris).
  2. Vous devrez jouer un peu avec les paramètres pour obtenir une image où les principaux composants/chiffres sont clairement visibles en tant que composants séparés. Presque parfait serait suffisant à ce stade.
  3. En considérant chaque Canny Edge comme un composant connecté (c'est-à-dire utiliser le cvFindContours () ou son homologue C++, selon le cas), on peut estimer les niveaux de gris de premier plan et d'arrière-plan et atteindre un seuil.

    Pour le dernier morceau, jetez un oeil aux sections 2. et 3. de cet article . En ignorant la plupart des parties théoriques non essentielles, il ne devrait pas être trop difficile de l'implémenter dans OpenCV.

    J'espère que cela vous a aidé!

Modifier 1:

Sur la base des seuils Canny Edge, voici une idée très approximative juste suffisante pour affiner les valeurs. Le high_threshold contrôle la force d'un Edge avant qu'il ne soit détecté. Fondamentalement, un Edge doit avoir une amplitude de gradient supérieure à high_threshold à détecter en premier lieu. Cela fait donc la détection initiale des bords.

Maintenant le low_threshold traite de la connexion des bords voisins. Il contrôle la quantité de bords déconnectés à proximité qui seront combinés en un seul bord. Pour une meilleure idée, lisez "Étape 6" de cette page Web . Essayez de définir un très petit seuil bas et voyez comment les choses se passent. Vous pourriez jeter cette chose de 0,66 * [valeur moyenne] si cela ne fonctionne pas sur ces images - ce n'est qu'une règle de base de toute façon.

2
AruniRC

La forme d'une ellipse est complexe à calculer par rapport à une forme plate. Essayer de changer:

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(19,19));

à:

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(19,19));

peut accélérer votre solution avec un faible impact sur la précision.

0
Duloren

Vous pouvez essayer de travailler sur une base par tuile si vous savez que vous avez un bon recadrage de la grille. Travailler sur 9 sous-images plutôt que sur la totalité de l'image entraînera très probablement une luminosité plus uniforme sur chaque sous-image. Si votre recadrage est parfait, vous pouvez même essayer d'aller pour chaque cellule de chiffres individuellement; mais tout dépend de la fiabilité de votre récolte.

0
amadillu