Ich habe eine Rechnungsdatei, ich möchte in jeder Rechnung eine Tabelle finden. Diese Tischposition wird nicht konstant sein. Also kam ich zur Bildverarbeitung. Zuerst habe ich versucht, meine Rechnung in ein Bild umzuwandeln. dann fand ich die kontur basierend auf tischrändern endlich tischposition. Ich habe den folgenden Code verwendet, um meine Aufgabe zu erfüllen.
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.
In diesem Code thresh1
, thresh2
werde ich basierend auf der Datei aktualisieren.
Mit diesem Code kann ich erfolgreich Positionen von Tabellen in Bildern lesen. Mit dieser Position arbeite ich an meiner PDF-Datei mit der Rechnung. Zum Beispiel
Probe 1:
Probe 2:
Ausgabe:
Probe 1:
Probe 2:
Probe 3:
Aber jetzt habe ich ein neues Format, das keine Grenzen hat, aber es ist eine Tabelle. Wie löse ich das? Weil meine gesamte Operation vollständig von Tabellenrändern abhängt. Aber jetzt habe ich keine Tischgrenzen. Wie kann ich das erreichen? Als Anfänger in der Bildverarbeitung habe ich keine Ahnung, dieses Problem zu lösen. Meine Frage ist: Gibt es eine Möglichkeit, eine Position basierend auf der Tabellenstruktur zu finden?
Zum Beispiel sieht meine Problemeingabe unten aus:
Ich würde gerne die folgende Piste finden:
Wie kann ich das lösen? Es ist wirklich wertvoll, mir eine Idee zu geben, um dieses Problem zu lösen.
Danke im Voraus.
Vaibhav hat recht. Sie können mit den verschiedenen morphologischen Transformationen experimentieren, um Pixel in verschiedene Formen, Linien usw. zu extrahieren oder zu gruppieren. Die Vorgehensweise kann beispielsweise folgendermaßen aussehen:
Ich habe ein kleines Beispiel zur Veranschaulichung der Idee geschrieben. Ich hoffe, der Code ist selbsterklärend. Ich habe dort auch einige Kommentare eingefügt.
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)
Ich habe folgende Ausgabe erhalten:
Um den Algorithmus robuster zu machen und auf eine Vielzahl verschiedener Eingangsbilder anwendbar zu machen, muss er natürlich entsprechend angepasst werden.
Sie können versuchen, einige morphologische Transformationen (wie Dilation, Erosion oder Gaußsche Unschärfe) als Vorverarbeitungsschritt vor der findContours-Funktion anzuwenden
Zum Beispiel
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)
Das letzte Argument, Iterationen, zeigt den Grad der Ausdehnung/Erosion (in Ihrem Fall im Text). Ein kleiner Wert führt zu kleinen unabhängigen Konturen, selbst innerhalb eines Alphabets, und große Werte werden viele nahegelegene Elemente vereinen. Sie müssen den idealen Wert finden, damit nur der Block Ihres Bildes erhalten wird.
Bitte beachten Sie, dass ich 150 als Schwellenwert genommen habe, da ich an der Extraktion von Text aus Bildern mit unterschiedlichen Hintergründen gearbeitet habe und dies besser geklappt hat. Sie können mit dem Wert fortfahren, den Sie genommen haben, da es sich um ein Schwarzweißbild handelt.