Maison  >  Article  >  développement back-end  >  OpenCV : Rechercher des colonnes dans des revues arabes (Python)

OpenCV : Rechercher des colonnes dans des revues arabes (Python)

WBOY
WBOYavant
2024-02-22 12:52:11628parcourir

OpenCV : Rechercher des colonnes dans des revues arabes (Python)

Contenu de la question

Je suis nouveau sur opencv et nouveau sur python. J'ai essayé de reconstituer le code que j'ai trouvé en ligne pour résoudre mon problème de recherche. J'ai un journal arabe de 1870 qui compte des centaines de pages, chaque page contient deux colonnes et a une épaisse bordure noire. Je souhaite extraire deux colonnes sous forme de fichiers image afin de pouvoir exécuter ocr dessus individuellement tout en ignorant l'en-tête et le pied de page. Voici un exemple de page :

Page 3

J’ai dix pages de l’impression originale sous forme de fichiers png séparés. J'ai écrit le script suivant pour gérer chacun d'entre eux. Cela fonctionne comme prévu dans 2 des 10 pages, mais ne parvient pas à générer les colonnes dans les 8 autres pages. Je ne comprends pas assez bien toutes les fonctions pour savoir où je pourrais utiliser ces valeurs, ou si toute mon approche est erronée - je pense que la meilleure façon d'apprendre est de demander à la communauté comment vous résoudriez ce problème.

import cv2

def cutpage(fname, pnum):
    image = cv2.imread(fname)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (7,7), 0)
    thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 13))
    dilate = cv2.dilate(thresh, kernel, iterations=1)
    dilatename = "temp/dilate" + str(pnum) + ".png"
    cv2.imwrite(dilatename, dilate)
    cnts = cv2.findContours(dilate, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    cnts = sorted(cnts, key=lambda x: cv2.boundingRect(x)[0])

    fullpage=1
    column=1
    for c in cnts:
        x, y, w, h = cv2.boundingRect(c)
        if h > 300 and w > 20:
            if (h/w)<2.5:
                print("Found full page: ", x, y, w, h)
                filename = "temp/p" + str(pnum) + "-full" + str(fullpage) + ".png"
                fullpage+=1
            else:
                print("Found column: ", x, y, w, h)
                filename = "temp/p" + str(pnum) + "-col" + str(column) + ".png"
                column+=1
            roi = image[y:y+h, x:x+w]
            cv2.imwrite(filename, roi)
    return (column-1)
        
for nr in range(10):
    filename = "p"+str(nr)+".png"
    print("Checking page", nr)
    diditwork = cutpage(filename, nr)
    print("Found", diditwork, "columns")

En suivant le tutoriel, j'ai créé une inversion binaire floue et dilatée afin qu'elle puisse identifier les différentes zones rectangulaires par la grande zone blanche. J'ai également enregistré une copie de chaque version étendue pour pouvoir voir à quoi elle ressemble, voici la page ci-dessus après traitement :

La page 3 a été agrandie

La boucle "for c in cnts" doit trouver de grandes zones rectangulaires dans l'image. Si le rapport hauteur/largeur est inférieur à 2,5, j'obtiens une page complète (sans en-tête ni pied de page, ce qui fonctionne bien), si le rapport hauteur/largeur est supérieur à cela, je sais que c'est une colonne et cela enregistre, par exemple temp/ p2-col2.png

.

J'ai obtenu de belles pages complètes sans en-têtes ni pieds de page, c'est-à-dire juste de grandes bordures noires, mais non découpées en colonnes. En 2 pages sur 10 j'ai obtenu ce que je voulais, à savoir :

Chronique du succès à la page 2

Étant donné que j'obtiens parfois les résultats souhaités, il doit y avoir quelque chose qui fonctionne, mais je ne sais pas comment l'améliorer davantage.

Éditeur :

Voici d'autres exemples de pages :

p0

p1

p5


Bonne réponse


J'ai essayé quelque chose sans aucune expansion parce que je voulais voir si je pouvais simplement utiliser la ligne médiane comme "séparateur". Voici le code :

im = cv2.cvtcolor(cv2.imread("arabic.png"), cv2.color_bgr2rgb) # read im as rgb for better plots
gray = cv2.cvtcolor(im, cv2.color_rgb2gray) # convert to gray
_, threshold = cv2.threshold(gray, 250, 255, cv2.thresh_binary_inv) # inverse thresholding
contours, _ = cv2.findcontours(threshold, cv2.retr_external, cv2.chain_approx_none) # find contours
sortedcontours = sorted(contours, key = cv2.contourarea, reverse=true) # sort according to area, descending
bigbox = sortedcontours[0] # get the contour of the big box
middleline = sortedcontours[1] # get the contour of the vertical line
xmiddleline, _, _, _ = cv2.boundingrect(middleline) # get x coordinate of middleline
leftboxcontour = np.array([point for point in bigbox if point[0, 0] < xmiddleline]) # assign left of line as points from the big contour
rightboxcontour = np.array([point for point in bigbox if point[0, 0] >= xmiddleline]) # assigh right of line as points from the big contour
leftboxx, leftboxy, leftboxw, leftboxh = cv2.boundingrect(leftboxcontour) # get properties of box on left
rightboxx, rightboxy, rightboxw, rightboxh = cv2.boundingrect(rightboxcontour) # get properties of box on right
leftboxcrop = im[leftboxy:leftboxy + leftboxh, leftboxx:leftboxx + leftboxw] # crop left 
rightboxcrop = im[rightboxy:rightboxy + rightboxh, rightboxx:rightboxx + rightboxw] # crop right
# maybe do you assertations about aspect ratio??
cv2.imwrite("right.png", rightboxcrop) # save image
cv2.imwrite("left.png", leftboxcrop) # save image

Je n'utilise aucune affirmation sur les proportions, alors peut-être que c'est toujours quelque chose que vous devez faire..

Fondamentalement, les lignes les plus importantes de cette méthode génèrent les contours gauche et droit en fonction de la coordonnée x. Voici le résultat final que j'ai obtenu :

Il y a encore quelques parties noires sur les bords, mais cela ne devrait pas poser de problème pour l'ocr.

Pour information : j'utilise les packages suivants dans jupyter :

import cv2
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt

v2.0 : Implémenté en utilisant uniquement la détection de grandes boîtes :

J'ai donc fait une dilatation et la grosse boîte était facilement détectable. J'utilise un noyau horizontal pour m'assurer que les lignes verticales de la grande boîte sont toujours suffisamment épaisses pour être détectées. Cependant, je n'arrive pas à résoudre le problème de la ligne médiane car elle est très fine... Voici néanmoins le code de la méthode ci-dessus :

im = cv2.cvtcolor(cv2.imread("1.png"), cv2.color_bgr2rgb) # read im as rgb for better plots
gray = cv2.cvtcolor(im, cv2.color_rgb2gray) # convert to gray
gray[gray<255] = 0 # added some contrast to make it either completly black or white
_, threshold = cv2.threshold(gray, 250, 255, cv2.thresh_binary_inv) # inverse thresholding
thresholddilated = cv2.dilate(threshold, np.ones((1,10)), iterations = 1) # dilate horizontally
contours, _ = cv2.findcontours(thresholddilated, cv2.retr_external, cv2.chain_approx_none) # find contours
sortedcontours = sorted(contours, key = cv2.contourarea, reverse=true) # sort according to area, descending
x, y, w, h = cv2.boundingrect(sortedcontours[0]) # get the bounding rect properties of the contour
left = im[y:y+h, x:x+int(w/2)+10].copy() # generate left, i included 10 pix from the right just in case
right = im[y:y+h, int(w/2)-10:w].copy() # and right, i included 10 pix from the left just in case
fig, ax = plt.subplots(nrows = 2, ncols = 3) # plotting...
ax[0,0].axis("off")
ax[0,1].imshow(im)
ax[0,1].axis("off")
ax[0,2].axis("off")
ax[1,0].imshow(left)
ax[1,0].axis("off")
ax[1,1].axis("off")
ax[1,2].imshow(right)
ax[1,2].axis("off")

Voici les résultats, vous pouvez remarquer que ce n'est pas parfait, mais encore une fois, puisque votre objectif est ocr, cela ne devrait pas poser de problème.

S'il vous plaît, dites-moi si cela fonctionne, sinon je vais me creuser la tête pour trouver une meilleure solution...

v3.0 : Une meilleure façon d'obtenir des images plus droites, ce qui améliorera la qualité de l'ocr.

Inspiré de mon autre réponse ici : answer. Il est logique de redresser l'image pour que l'ocr donne de meilleurs résultats. Par conséquent, j'ai utilisé une transformation en quatre points sur le cadre extérieur détecté. Cela redressera légèrement l'image et rendra le texte plus horizontal. Voici le code :

im = cv2.cvtcolor(cv2.imread("2.png"), cv2.color_bgr2rgb) # read im as rgb for better plots
gray = cv2.cvtcolor(im, cv2.color_rgb2gray) # convert to gray
gray[gray<255] = 0 # added some contrast to make it either completly black or white
_, threshold = cv2.threshold(gray, 250, 255, cv2.thresh_binary_inv) # inverse thresholding
thresholddilated = cv2.dilate(threshold, np.ones((1,10)), iterations = 1) # dilate horizontally
contours, _ = cv2.findcontours(thresholddilated, cv2.retr_external, cv2.chain_approx_none) # find contours
largest_contour = max(contours, key = cv2.contourarea) # get largest contour
hull = cv2.convexhull(largest_contour) # get the hull
epsilon = 0.02 * cv2.arclength(largest_contour, true) # epsilon
pts1 = np.float32(cv2.approxpolydp(hull, epsilon, true).reshape(-1, 2)) # get the points
result = four_point_transform(im, pts1) # using imutils
height, width = result.shape[:2] # get the dimensions of the transformed image
left = result[:, 0:int(width/2)].copy() # from the beginning to half the width
right = result[:, int(width/2): width].copy() # from half the width till the end
fig, ax = plt.subplots(nrows = 2, ncols = 3) # plotting...
ax[0,0].axis("off")
ax[0,1].imshow(result)
ax[0,1].axvline(width/2)
ax[0,1].axis("off")
ax[0,2].axis("off")
ax[1,0].imshow(left)
ax[1,0].axis("off")
ax[1,1].axis("off")
ax[1,2].imshow(right)
ax[1,2].axis("off")

Comprend les forfaits suivants :

import cv2
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt
from imutils.perspective import four_point_transform

Comme vous pouvez le voir sur le code, c'est une meilleure approche, vous pouvez forcer l'image à être centrée et horizontale grâce à la transformation en quatre points. De plus, il n’est pas nécessaire d’inclure un certain chevauchement puisque les images sont bien séparées. Voici un exemple pour votre référence :

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer