J'essaie d'extraire du texte d'une image à l'aide de Tesseract OCR. Actuellement, avec l'image d'entrée d'origine (comme ci-dessous), la qualité de sortie est très mauvaise (environ 50%). Mais lorsque j'essaie de supprimer toutes les lignes et les bordures de l'image d'entrée (à l'aide de Photoshop), la sortie s'améliore beaucoup (~ 90%). Existe-t-il un moyen de supprimer toutes les lignes et bordures de l'image (conserver les textes) par programme (en utilisant OpenCV, Image magick, ..)?
Image originale:
Attendre l'image:
Ne pas utiliser OpenCV, mais juste une ligne d'ImageMagick dans le terminal, mais cela peut vous donner une idée de la façon de le faire dans OpenCV. ImageMagick est installé sur la plupart des distributions Linux et est disponible pour OSX et Windows.
Le nœud du concept est de créer une nouvelle image où chaque pixel est réglé sur la médiane des 100 pixels voisins à sa gauche et des 100 pixels voisins à sa droite. De cette façon, les pixels qui ont beaucoup de voisins horizontaux qui sont noirs (c'est-à-dire des lignes noires horizontales) seront blancs dans l'image de sortie. Ensuite, le même traitement est appliqué dans le sens vertical pour supprimer les lignes verticales.
La commande que vous saisissez dans le terminal sera:
convert input.png \
\( -clone 0 -threshold 50% -negate -statistic median 200x1 \) \
-compose lighten -composite \
\( -clone 0 -threshold 50% -negate -statistic median 1x200 \) \
-composite result.png
La première ligne indique de charger votre image d'origine.
La deuxième ligne commence certains "traitement de côté" qui copie l'image originale, la seuil et l'inverse, puis la médiane de tous les pixels voisins 100 de chaque côté est calculé.
La troisième ligne prend ensuite le résultat de la deuxième ligne et la compose sur l'image d'origine, en choisissant le plus clair des pixels à chaque emplacement - c'est-à-dire ceux que mon masque de ligne horizontale a blanchis.
Les deux lignes suivantes font à nouveau la même chose mais orientées verticalement pour les lignes verticales.
Le résultat est comme ceci:
Si je fais une différence avec votre image d'origine, comme celle-ci, je peux voir ce qu'elle a fait:
convert input.png result.png -compose difference -composite diff.png
Je suppose que si vous vouliez supprimer un peu plus de lignes, vous pourriez en fait flouter un peu l'image de différence et l'appliquer à l'original. Bien sûr, vous pouvez jouer avec les longueurs de filtre et les seuils et tout ça aussi.
Il existe une meilleure façon de le faire avec ImageMagick.
ImageMagick a une fonctionnalité intéressante, appelée Morphologie des formes. Vous pouvez l'utiliser pour identifier des formes comme des lignes de table et les supprimer.
convert in.png \
-type Grayscale \
-negate \
-define morphology:compose=darken \
-morphology Thinning 'Rectangle:1x80+0+0<' \
-negate \
out.png
Après avoir postulé
convert in.png -type Grayscale -negate -define morphology:compose=darken -morphology Thinning 'Rectangle:1x80+0+0<' -negate out.png
c'était l'image de sortie:
Face au même problème. Et je pense qu'une solution plus logique pourrait être (Référence: Extraire les bordures du tablea )
//assuming, b_w is the binary image
inv = 255 - b_w
horizontal_img = new_img
vertical_img = new_img
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (100,1))
horizontal_img = cv2.erode(horizontal_img, kernel, iterations=1)
horizontal_img = cv2.dilate(horizontal_img, kernel, iterations=1)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,100))
vertical_img = cv2.erode(vertical_img, kernel, iterations=1)
vertical_img = cv2.dilate(vertical_img, kernel, iterations=1)
mask_img = horizontal_img + vertical_img
no_border = np.bitwise_or(b_w, mask_img)
Puisque personne n'a publié de solution OpenCV complète, voici une approche simple
Obtenez une image binaire. Chargez l'image, convertissez-la en niveaux de gris et seuil d'Ots
Supprimer les lignes horizontales. Nous créons un noyau de forme horizontale avec cv2.getStructuringElement
puis trouver les contours et supprimez les lignes avec cv2.drawContours
Supprimer les lignes verticales. Nous faisons la même opération mais avec un noyau de forme verticale
Charger l'image, convertir en niveaux de gris, puis seuil d'Ots pour obtenir une image binaire
image = cv2.imread('1.png')
result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
Maintenant, nous créons un noyau horizontal pour détecter les lignes horizontales avec cv2.getStructuringElement()
et trouver les contours avec cv2.findContours()
. Pour supprimer les lignes horizontales, nous utilisons cv2.drawContours()
et remplissons chaque contour horizontal de blanc. Cela "efface" effectivement la ligne horizontale. Voici les lignes horizontales détectées en vert
# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
De même, nous créons un noyau vertical pour supprimer les lignes verticales, trouver les contours et remplir chaque contour vertical de blanc. Voici les lignes verticales détectées surlignées en vert
# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,40))
remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
Après avoir rempli les lignes horizontales et verticales avec du blanc, voici notre résultat
Remarque: Selon l'image, vous devrez peut-être modifier la taille du noyau. Par exemple, pour capturer des lignes horizontales plus longues, il peut être nécessaire d'augmenter le noyau horizontal de (40, 1)
Pour dire (80, 1)
. Si vous vouliez détecter des lignes horizontales plus épaisses, vous pourriez augmenter la largeur du noyau pour dire (80, 2)
. De plus, vous pouvez augmenter le nombre d'itérations lors de l'exécution de cv2.morphologyEx()
. De même, vous pouvez modifier les noyaux verticaux pour détecter des lignes plus ou moins verticales. Il y a un compromis lorsque vous augmentez ou diminuez la taille du noyau car vous pouvez capturer plus ou moins de lignes. Encore une fois, tout varie en fonction de l'image d'entrée
Code complet pour l'exhaustivité
import cv2
image = cv2.imread('1.png')
result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,40))
remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.imwrite('result.png', result)
cv2.waitKey()
Vous pouvez utiliser un algorithme de détection de bord de Sobel/Laplacian/Canny et utiliser la transformation de Hough pour identifier les lignes dans OpenCV et les colorer en blanc pour supprimer les lignes:
laplacian = cv2.Laplacian(img,cv2.CV_8UC1) # Laplacian OR
edges = cv2.Canny(img,80,10,apertureSize = 3) # canny Edge OR
# Output dtype = cv2.CV_8U # Sobel
sobelx8u = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=5)
# Output dtype = cv2.CV_64F. Then take its absolute and convert to cv2.CV_8U
sobelx64f = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
# Hough's Probabilistic Line Transform
minLineLength = 900
maxLineGap = 100
lines = cv2.HoughLinesP(edges,1,np.pi/180,100,minLineLength,maxLineGap)
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(img,(x1,y1),(x2,y2),(255,255,255),2)
cv2.imwrite('houghlines.jpg',img)
Ce dont vous avez besoin est Leptonica et Lept4j .
Il y a un exemple sur la façon d'accomplir cela dans le code source du projet, dans les tests ici: LineRemovalTest.Java
Contribution:
production:
J'ai une idée. Mais cela ne fonctionnera que si vous avez des lignes absolument horizontales et verticales. Vous pouvez d'abord faire une binarisation sur cette image (si ce n'est pas déjà fait). Ensuite, écrivez du code qui itère à travers chaque ligne de l'image en même temps en vérifiant s'il y a une séquence de pixels noirs contenant plus d'un certain seuil. Par exemple, s'il y a une séquence continue de points noirs sur une ligne à partir du 100e pixel au 150e pixel, rendez ces pixels blancs. Après avoir trouvé toutes les lignes horizontales, vous pouvez faire de même pour vous débarrasser des lignes verticales.
Ici, dans mon exemple, je considère que la séquence de pixels noirs commence exactement à partir du 100e pixel et se termine au 150e parce que s'il y a un autre pixel noir au 151e pixel, je dois également ajouter ce pixel. En d'autres termes, essayez de trouver les lignes complètement.
Si vous résolvez cette question, veuillez me le faire savoir)