J'ai un fichier de factures, je veux trouver un tableau dans chaque facture. cette position dans la table ne sera pas constante. Je suis donc venu au traitement d'images. J'ai d'abord essayé de convertir ma facture en image. puis j'ai trouvé le contour basé sur les bordures de la table qui a finalement pris la position de la table. J'ai utilisé le code ci-dessous pour accomplir ma tâche.
with Image(page) as page_image:
page_image.alpha_channel = False #eliminates transperancy
img_buffer=np.asarray(bytearray(page_image.make_blob()), dtype=np.uint8)
img = cv2.imdecode(img_buffer, cv2.IMREAD_UNCHANGED)
ret, thresh = cv2.threshold(img, 127, 255, 0)
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
margin=[]
for contour in contours:
# get rectangle bounding contour
[x, y, w, h] = cv2.boundingRect(contour)
# Don't plot small false positives that aren't text
if (w >thresh1 and h> thresh2):
margin.append([x, y, x + w, y + h])
#data cleanup on margin to extract required position values.
Dans ce code thresh1
, thresh2
je mettrai à jour en fonction du fichier.
Donc, en utilisant ce code, je peux lire avec succès les positions des tableaux dans les images, en utilisant cette position, je vais travailler sur mon fichier pdf de facture. Par exemple
Échantillon 1:
Échantillon 2:
Sortie:
Échantillon 1:
Échantillon 2:
Échantillon 3:
Mais maintenant, j'ai un nouveau format qui n'a pas de frontière mais c'est un tableau. Comment résoudre ceci? Parce que toute mon opération dépend entièrement des limites de la table. Mais maintenant, je n'ai pas de bordures de table. Comment puis-je atteindre cet objectif? En tant que débutant en traitement d’image, je n’ai aucune idée de sortir de ce problème. Ma question est la suivante: y a-t-il un moyen de trouver une position basée sur la structure de la table?
Par exemple, l'entrée de mon problème est présentée ci-dessous:
Je voudrais trouver sa poistion comme ci-dessous:
Comment puis-je résoudre ce problème? C'est vraiment appréciable de me donner une idée à résoudre à partir de ce problème.
Merci d'avance.
Vaibhav a raison. Vous pouvez expérimenter différentes transformations morphologiques pour extraire ou grouper des pixels dans différentes formes, lignes, etc. Par exemple, l'approche peut être la suivante:
J'ai écrit un petit échantillon illustrant l'idée. J'espère que le code est explicite. J'ai mis quelques commentaires là aussi.
import os
import cv2
import imutils
# This only works if there's only one table on a page
# Important parameters:
# - morph_size
# - min_text_height_limit
# - max_text_height_limit
# - cell_threshold
# - min_columns
def pre_process_image(img, save_in_file, morph_size=(8, 8)):
# get rid of the color
pre = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Otsu threshold
pre = cv2.threshold(pre, 250, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# dilate the text to make it solid spot
cpy = pre.copy()
struct = cv2.getStructuringElement(cv2.MORPH_RECT, morph_size)
cpy = cv2.dilate(~cpy, struct, anchor=(-1, -1), iterations=1)
pre = ~cpy
if save_in_file is not None:
cv2.imwrite(save_in_file, pre)
return pre
def find_text_boxes(pre, min_text_height_limit=6, max_text_height_limit=40):
# Looking for the text spots contours
contours = cv2.findContours(pre, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if imutils.is_cv2() else contours[1]
# Getting the texts bounding boxes based on the text size assumptions
boxes = []
for contour in contours:
box = cv2.boundingRect(contour)
h = box[3]
if min_text_height_limit < h < max_text_height_limit:
boxes.append(box)
return boxes
def find_table_in_boxes(boxes, cell_threshold=10, min_columns=2):
rows = {}
cols = {}
# Clustering the bounding boxes by their positions
for box in boxes:
(x, y, w, h) = box
col_key = x // cell_threshold
row_key = y // cell_threshold
cols[row_key] = [box] if col_key not in cols else cols[col_key] + [box]
rows[row_key] = [box] if row_key not in rows else rows[row_key] + [box]
# Filtering out the clusters having less than 2 cols
table_cells = list(filter(lambda r: len(r) >= min_columns, rows.values()))
# Sorting the row cells by x coord
table_cells = [list(sorted(tb)) for tb in table_cells]
# Sorting rows by the y coord
table_cells = list(sorted(table_cells, key=lambda r: r[0][1]))
return table_cells
def build_lines(table_cells):
if table_cells is None or len(table_cells) <= 0:
return [], []
max_last_col_width_row = max(table_cells, key=lambda b: b[-1][2])
max_x = max_last_col_width_row[-1][0] + max_last_col_width_row[-1][2]
max_last_row_height_box = max(table_cells[-1], key=lambda b: b[3])
max_y = max_last_row_height_box[1] + max_last_row_height_box[3]
hor_lines = []
ver_lines = []
for box in table_cells:
x = box[0][0]
y = box[0][1]
hor_lines.append((x, y, max_x, y))
for box in table_cells[0]:
x = box[0]
y = box[1]
ver_lines.append((x, y, x, max_y))
(x, y, w, h) = table_cells[0][-1]
ver_lines.append((max_x, y, max_x, max_y))
(x, y, w, h) = table_cells[0][0]
hor_lines.append((x, max_y, max_x, max_y))
return hor_lines, ver_lines
if __== "__main__":
in_file = os.path.join("data", "page.jpg")
pre_file = os.path.join("data", "pre.png")
out_file = os.path.join("data", "out.png")
img = cv2.imread(os.path.join(in_file))
pre_processed = pre_process_image(img, pre_file)
text_boxes = find_text_boxes(pre_processed)
cells = find_table_in_boxes(text_boxes)
hor_lines, ver_lines = build_lines(cells)
# Visualize the result
vis = img.copy()
# for box in text_boxes:
# (x, y, w, h) = box
# cv2.rectangle(vis, (x, y), (x + w - 2, y + h - 2), (0, 255, 0), 1)
for line in hor_lines:
[x1, y1, x2, y2] = line
cv2.line(vis, (x1, y1), (x2, y2), (0, 0, 255), 1)
for line in ver_lines:
[x1, y1, x2, y2] = line
cv2.line(vis, (x1, y1), (x2, y2), (0, 0, 255), 1)
cv2.imwrite(out_file, vis)
J'ai la sortie suivante:
Bien sûr, pour rendre l'algorithme plus robuste et applicable à une variété d'images d'entrée différentes, il doit être ajusté en conséquence.
Vous pouvez essayer d'appliquer certaines transformations morphologiques (telles que Dilatation, Érosion ou Flou Gaussien) comme étape de pré-traitement avant votre fonction findContours.
Par exemple
blur = cv2.GaussianBlur(g, (3, 3), 0)
ret, thresh1 = cv2.threshold(blur, 150, 255, cv2.THRESH_BINARY)
bitwise = cv2.bitwise_not(thresh1)
erosion = cv2.erode(bitwise, np.ones((1, 1) ,np.uint8), iterations=5)
dilation = cv2.dilate(erosion, np.ones((3, 3) ,np.uint8), iterations=5)
Le dernier argument, les itérations, indique le degré de dilatation/érosion qui aura lieu (dans votre cas, sur le texte). Avoir une petite valeur aura pour résultat de petits contours indépendants même au sein d'un alphabet et de grandes valeurs encombreront de nombreux éléments proches. Vous devez trouver la valeur idéale pour ne recevoir que ce bloc de votre image.
Veuillez noter que j'ai pris 150 comme paramètre de seuil parce que je travaillais à extraire du texte à partir d'images avec différents arrière-plans et que cela fonctionnait mieux. Vous pouvez choisir de continuer avec la valeur que vous avez prise car il s'agit d'une image en noir et blanc.