web-dev-qa-db-fra.com

Détecter une zone de texte dans une image avec python et opencv

Je souhaite détecter la zone de texte des images à l’aide de python 2.7 et de l'opencv 2.4.9), puis dessiner une zone rectangle autour de celle-ci. Comme illustré dans l'exemple ci-dessous.

Je suis nouveau dans le traitement des images, alors toute idée de ce que vous allez faire sera appréciée.

building blueprint with labeled rooms

27
User9412

Il y a plusieurs façons de détecter du texte dans une image.

Je recommande de regarder cette question ici , car cela pourrait également répondre à votre cas. Bien qu’il ne soit pas en python, le code peut facilement être traduit de c ++ en python (il suffit de regarder l’API et de convertir les méthodes de c ++ en python, pas difficile. Je l’ai fait moi-même essayé leur code pour mon propre problème séparé). Les solutions ici peuvent ne pas fonctionner pour votre cas, mais je recommande de les essayer.

Si je devais y aller, je ferais le processus suivant:

Préparez votre image: si toutes vos images que vous souhaitez modifier sont à peu près identiques à celle que vous avez fournie, dans laquelle le dessin actuel est constitué d’une gamme de couleurs grises et où le texte est toujours noir. Je voudrais tout d'abord masquer tout contenu qui n'est pas noir (ou déjà blanc). Cela ne laissera que le texte noir à gauche.

# must import if working with opencv in python
import numpy as np
import cv2

# removes pixels in image that are between the range of
# [lower_val,upper_val]
def remove_gray(img,lower_val,upper_val):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    lower_bound = np.array([0,0,lower_val])
    upper_bound = np.array([255,255,upper_val])
    mask = cv2.inRange(gray, lower_bound, upper_bound)
    return cv2.bitwise_and(gray, gray, mask = mask)

Maintenant que tout ce que vous avez est le texte en noir, l'objectif est d'obtenir ces boîtes. Comme indiqué précédemment, il existe différentes façons de procéder.

Transformation de largeur de trait (SWT)

Le moyen typique de rechercher des zones de texte: vous pouvez trouver des régions de texte en utilisant la transformation de largeur de trait comme décrit dans "Détection de texte dans des scènes naturelles avec transformation de largeur de trait" par Boris Epshtein, Eyal Ofek et Yonatan Wexler. Pour être honnête, si cette méthode est aussi rapide et fiable que je le crois, cette méthode est plus efficace que mon code ci-dessous. Vous pouvez toujours utiliser le code ci-dessus pour supprimer le plan détaillé, ce qui peut améliorer les performances globales de l'algorithme swt.

Voici une bibliothèque c qui implémente leur algorithme, mais il est dit qu'il est très brut et que la documentation est dite incomplète. De toute évidence, un wrapper sera nécessaire pour utiliser cette bibliothèque avec Python, et pour le moment je ne vois pas de bibliothèque officielle offerte.

La bibliothèque que j'ai liée est CCV . C'est une bibliothèque destinée à être utilisée dans vos applications, pas à recréer des algorithmes. C’est donc un outil à utiliser qui va à l’encontre du souhait des OP de le faire à partir de "Premiers Principes", comme indiqué dans les commentaires. Néanmoins, il est utile de savoir qu’il existe si vous ne voulez pas coder vous-même l’algorithme.


Méthode brassée à la maison sans SWT

Si vous avez des métadonnées pour chaque image, par exemple dans un fichier XML, indiquant le nombre de pièces étiquetées dans chaque image, vous pouvez accéder à ce fichier XML, obtenir les données relatives au nombre d'étiquettes dans l'image, puis les stocker. nombre dans une variable disons, num_of_labels. Maintenant, prenez votre image et passez-la dans une boucle while qui s'érode à la fréquence que vous spécifiez, en recherchant des contours externes dans l'image de chaque boucle et en arrêtant la boucle une fois que vous avez le même nombre de contours externes que votre num_of_labels. Ensuite, il vous suffit de trouver le cadre de sélection de chaque contour et vous avez terminé.

# erodes image based on given kernel size (erosion = expands black areas)
def erode( img, kern_size = 3 ):
    retval, img = cv2.threshold(img, 254.0, 255.0, cv2.THRESH_BINARY) # threshold to deal with only black and white.
    kern = np.ones((kern_size,kern_size),np.uint8) # make a kernel for erosion based on given kernel size.
    eroded = cv2.erode(img, kern, 1) # erode your image to blobbify black areas
    y,x = eroded.shape # get shape of image to make a white boarder around image of 1px, to avoid problems with find contours.
    return cv2.rectangle(eroded, (0,0), (x,y), (255,255,255), 1)

# finds contours of eroded image
def prep( img, kern_size = 3 ):    
    img = erode( img, kern_size )
    retval, img = cv2.threshold(img, 200.0, 255.0, cv2.THRESH_BINARY_INV) #   invert colors for findContours
    return cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # Find Contours of Image

# given img & number of desired blobs, returns contours of blobs.
def blobbify(img, num_of_labels, kern_size = 3, dilation_rate = 10):
    prep_img, contours, hierarchy = prep( img.copy(), kern_size ) # dilate img and check current contour count.
    while len(contours) > num_of_labels:
        kern_size += dilation_rate # add dilation_rate to kern_size to increase the blob. Remember kern_size must always be odd.
        previous = (prep_img, contours, hierarchy)
        processed_img, contours, hierarchy = prep( img.copy(), kern_size ) # dilate img and check current contour count, again.
    if len(contours) < num_of_labels:
        return (processed_img, contours, hierarchy)
    else:
        return previous

# finds bounding boxes of all contours
def bounding_box(contours):
    bBox = []
    for curve in contours:
        box = cv2.boundingRect(curve)
    bBox.append(box)
    return bBox

Les cases résultantes de la méthode ci-dessus laisseront un espace autour des étiquettes, ce qui peut inclure une partie du dessin d'origine si les cases sont appliquées à l'image originale. Pour éviter cela, créez des zones d’intérêt via vos nouvelles boîtes trouvées et coupez l’espace blanc. Enregistrez ensuite la forme de ce roi dans votre nouvelle boîte.

Peut-être n’avez-vous aucun moyen de savoir combien d’étiquettes figureront dans l’image. Si tel est le cas, je vous recommande de jouer avec les valeurs d'érosion jusqu'à ce que vous trouviez celle qui convient le mieux à votre cas et que vous obteniez les blobs souhaités.

Vous pouvez également essayer de trouver des contours sur le contenu restant après avoir supprimé le motif et combiner des cadres de sélection dans un rectangle en fonction de leur distance.

Une fois que vous avez trouvé vos boîtes, utilisez-les simplement en respectant l’image d’origine et vous aurez terminé.


Module de détection de texte de scène dans OpenCV 3

Comme mentionné dans les commentaires de votre question, il existe déjà un moyen de détection de texte de scène (non de détection de texte de document) dans l'openv 3. Je comprends que vous ne pouvez pas changer de version, mais pour ceux avec la même question et sans limitation. dans une version plus ancienne, j’ai décidé de l’inclure à la fin. La documentation pour la détection de texte de scène peut être trouvée avec une simple recherche google.

Le module opencv pour la détection de texte est également livré avec une reconnaissance de texte qui implémente tessaract, qui est un module gratuit de reconnaissance de texte à source ouverte. La chute de tessaract, et par conséquent du module de reconnaissance de texte de scène d’opencv, c’est qu’il n’est pas aussi raffiné que les applications commerciales et prend beaucoup de temps. Diminuant ainsi ses performances, mais libre d’utilisation, c’est le meilleur que nous ayons obtenu sans payer d’argent, si vous souhaitez également une reconnaissance de texte.

Liens:

Honnêtement, je manque d’expérience et de connaissances spécialisées en traitement de l’image et en traitement de l’image afin de fournir une méthode détaillée pour la mise en œuvre de leur module de détection de texte. La même chose avec l'algorithme SWT. Je viens tout juste de m'y intéresser depuis quelques mois, mais au fur et à mesure que j'apprends, je vais modifier cette réponse.

38
prijatelj