Obtenez l'emplacement de tout le texte présent dans l'image en utilisant opencv

11

J'ai cette image qui contient du texte (chiffres et alphabets). Je veux obtenir l'emplacement de tous les textes et nombres présents dans cette image. Je veux également extraire tout le texte.

entrez la description de l'image ici

Comment obtenir les coordonnées ainsi que tout le texte (chiffres et alphabets) de mon image. Pour par exemple 10B, 44, 16, 38, 22B etc.

Pulkit Bhatnagar
la source
quelle est votre version tensorflow? Si votre version est 2.1, essayez d'installer 2.0
gellezzz
1
Lancer des primes sur de mauvaises questions n'est pas une bonne pratique. Vous n'avez montré aucune connaissance sur la façon de procéder, il semble que vous essayez simplement d'attirer les développeurs dans le codage d'une solution complète en échange de quelques points de répétition. Je ne m'attends pas à voir des réponses parfaites pour cette raison, mais je crois que vous pouvez obtenir de meilleures solutions sur les sites Web indépendants si vous payez des personnes pour leur temps.
karlphillip
@karlphillip désolé, mais je suis un débutant, j'ai besoin de quelque chose pour commencer, non? Pouvez-vous m'aider avec celui-ci
Pulkit Bhatnagar

Réponses:

13

Voici une approche potentielle utilisant des opérations morphologiques pour filtrer les contours non textuels. L'idée est:

  1. Obtenez une image binaire. Charger l'image, niveaux de gris, puis le seuil d'Otsu

  2. Supprimez les lignes horizontales et verticales. Créez des noyaux horizontaux et verticaux en utilisant cv2.getStructuringElementpuis supprimez les lignes aveccv2.drawContours

  3. Supprimez les lignes diagonales, les objets circulaires et les contours courbes. Filtre utilisant la zone de contour cv2.contourArea et l'approximation de contour cv2.approxPolyDP pour isoler les contours non textuels

  4. Extraire les ROI et l'OCR du texte. Trouvez les contours et filtrez les ROI puis l'OCR en utilisant Pytesseract .


Suppression des lignes horizontales surlignées en vert

entrez la description de l'image ici

Suppression des lignes verticales

entrez la description de l'image ici

Suppression des contours non textuels assortis (lignes diagonales, objets circulaires et courbes)

entrez la description de l'image ici

Régions de texte détectées

entrez la description de l'image ici

import cv2
import numpy as np
import pytesseract

pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
clean = thresh.copy()

# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,30))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

cnts = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    # Remove diagonal lines
    area = cv2.contourArea(c)
    if area < 100:
        cv2.drawContours(clean, [c], -1, 0, 3)
    # Remove circle objects
    elif area > 1000:
        cv2.drawContours(clean, [c], -1, 0, -1)
    # Remove curve stuff
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    x,y,w,h = cv2.boundingRect(c)
    if len(approx) == 4:
        cv2.rectangle(clean, (x, y), (x + w, y + h), 0, -1)

open_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
opening = cv2.morphologyEx(clean, cv2.MORPH_OPEN, open_kernel, iterations=2)
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,2))
close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, close_kernel, iterations=4)
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = cv2.contourArea(c)
    if area > 500:
        ROI = image[y:y+h, x:x+w]
        ROI = cv2.GaussianBlur(ROI, (3,3), 0)
        data = pytesseract.image_to_string(ROI, lang='eng',config='--psm 6')
        if data.isalnum():
            cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
            print(data)

cv2.imwrite('image.png', image)
cv2.imwrite('clean.png', clean)
cv2.imwrite('close.png', close)
cv2.imwrite('opening.png', opening)
cv2.waitKey()
nathancy
la source
Bonne idée de supprimer ces lignes en premier.
karlphillip
mauvaise idée de retirer aussi des parties de lettres ...
Walter Tross
8

D'accord, voici une autre solution possible. Je sais que vous travaillez avec Python - je travaille avec C ++. Je vais vous donner quelques idées et j'espère que si vous le souhaitez, vous pourrez mettre en œuvre cette réponse.

L'idée principale est de ne pas utiliser du tout le prétraitement (du moins pas au stade initial) et de se concentrer à la place sur chaque personnage cible, d'obtenir certaines propriétés et de filtrer chaque blob en fonction de ces propriétés.

J'essaie de ne pas utiliser le prétraitement car: 1) les filtres et les étapes morphologiques pourraient dégrader la qualité des blobs et 2) vos blobs cibles semblent présenter certaines caractéristiques que nous pourrions exploiter, principalement: le rapport d'aspect et la zone .

Vérifiez-le, les chiffres et les lettres semblent tous être plus grands que plus larges… en outre, ils semblent varier dans une certaine valeur de zone. Par exemple, vous souhaitez supprimer les objets "trop ​​larges" ou "trop ​​gros" .

L'idée est que je filtre tout ce qui ne correspond pas aux valeurs pré-calculées. J'ai examiné les caractères (chiffres et lettres) et suis venu avec des valeurs minimales et maximales de surface et un rapport d'aspect minimum (ici, le rapport entre la hauteur et la largeur).

Travaillons sur l'algorithme. Commencez par lire l'image et redimensionnez-la à la moitié des dimensions. Votre image est bien trop grande. Convertissez en niveaux de gris et obtenez une image binaire via otsu, voici en pseudo-code:

//Read input:
inputImage = imread( "diagram.png" );

//Resize Image;
resizeScale = 0.5;

inputResized = imresize( inputImage, resizeScale );

//Convert to grayscale;
inputGray = rgb2gray( inputResized );

//Get binary image via otsu:
binaryImage = imbinarize( inputGray, "Otsu" );

Cool. Nous allons travailler avec cette image. Vous devez examiner chaque goutte blanche et appliquer un "filtre de propriétés" . J'utilise des composants connectés avec des statistiques pour parcourir chaque blob et obtenir sa zone et son rapport d'aspect, en C ++, cela se fait comme suit:

//Prepare the output matrices:
cv::Mat outputLabels, stats, centroids;
int connectivity = 8;

//Run the binary image through connected components:
int numberofComponents = cv::connectedComponentsWithStats( binaryImage, outputLabels, stats, centroids, connectivity );

//Prepare a vector of colors  color the filtered blobs in black
std::vector<cv::Vec3b> colors(numberofComponents+1);
colors[0] = cv::Vec3b( 0, 0, 0 ); // Element 0 is the background, which remains black.

//loop through the detected blobs:
for( int i = 1; i <= numberofComponents; i++ ) {

    //get area:
    auto blobArea = stats.at<int>(i, cv::CC_STAT_AREA);

    //get height, width and compute aspect ratio:
    auto blobWidth = stats.at<int>(i, cv::CC_STAT_WIDTH);
    auto blobHeight = stats.at<int>(i, cv::CC_STAT_HEIGHT);
    float blobAspectRatio = (float)blobHeight/(float)blobWidth;

    //Filter your blobs

};

Maintenant, nous allons appliquer le filtre des propriétés. Ce n'est qu'une comparaison avec les seuils pré-calculés. J'ai utilisé les valeurs suivantes:

Minimum Area: 40  Maximum Area:400
MinimumAspectRatio:  1

Dans votre forboucle, comparez les propriétés de blob actuelles avec ces valeurs. Si les tests sont positifs, vous "peignez" le blob en noir. Continuer à l'intérieur de la forboucle:

    //Filter your blobs

    //Test the current properties against the thresholds:
    bool areaTest =  (blobArea > maxArea)||(blobArea < minArea);
    bool aspectRatioTest = !(blobAspectRatio > minAspectRatio); //notice we are looking for TALL elements!

    //Paint the blob black:
    if( areaTest || aspectRatioTest ){
        //filtered blobs are colored in black:
        colors[i] = cv::Vec3b( 0, 0, 0 );
    }else{
        //unfiltered blobs are colored in white:
        colors[i] = cv::Vec3b( 255, 255, 255 );
    }

Après la boucle, construisez l'image filtrée:

cv::Mat filteredMat = cv::Mat::zeros( binaryImage.size(), CV_8UC3 );
for( int y = 0; y < filteredMat.rows; y++ ){
    for( int x = 0; x < filteredMat.cols; x++ )
    {
        int label = outputLabels.at<int>(y, x);
        filteredMat.at<cv::Vec3b>(y, x) = colors[label];
    }
}

Et c'est à peu près tout. Vous avez filtré tous les éléments qui ne sont pas similaires à ce que vous recherchez. En exécutant l'algorithme, vous obtenez ce résultat:

entrez la description de l'image ici

J'ai également trouvé les boîtes englobantes des blobs pour mieux visualiser les résultats:

entrez la description de l'image ici

Comme vous le voyez, certains éléments sont mal détectés. Vous pouvez affiner le "filtre des propriétés" pour mieux identifier les caractères que vous recherchez. Une solution plus approfondie, impliquant un peu d'apprentissage automatique, nécessite la construction d'un "vecteur de caractéristiques idéal", extrayant les caractéristiques des blobs et comparant les deux vecteurs via une mesure de similarité. Vous pouvez également appliquer un post- traitement pour améliorer les résultats ...

Quoi qu'il en soit, mec, votre problème n'est pas trivial ni facile à évoluer, et je vous donne juste des idées. Espérons que vous pourrez mettre en œuvre votre solution.

eldesgraciado
la source
Toute chance que vous pouvez convertir le même programme en python
Pulkit Bhatnagar
@PulkitBhatnagar Oui, bien sûr. Tenez-vous bien, je vais avoir un port parfait prêt dans quelques minutes.
eldesgraciado
?? l'avez-vous fait, pour que je puisse vous attribuer la prime
Pulkit Bhatnagar
Ah oui. Je suis terriblement désolé, monsieur, j'ai rencontré des ennuis, mais la conversion se déroule bien. Attends. THX.
eldesgraciado
Je ne me suis jamais demandé si cela pouvait être un sarcasme.
Pulkit Bhatnagar
4

Une méthode consiste à utiliser une fenêtre coulissante (c'est cher).

Déterminez la taille des caractères de l'image (tous les caractères sont de la même taille que celle indiquée dans l'image) et définissez la taille de la fenêtre. Essayez tesseract pour la détection (l'image d'entrée nécessite un prétraitement). Si une fenêtre détecte des caractères consécutivement, stockez les coordonnées de la fenêtre. Fusionnez les coordonnées et obtenez la région sur les personnages.

sauver la Terre
la source
Je pense que 100bounty est pour la réponse
Himanshu Poddar