Trích xuất thông tin văn bản từ các tệp PDF với các bố cục khác nhau - học máy


8

Tôi cần hỗ trợ với một dự án ML mà tôi hiện đang cố gắng tạo.

Tôi nhận được rất nhiều hóa đơn từ rất nhiều nhà cung cấp khác nhau - tất cả đều được bố trí độc đáo. Tôi cần trích xuất 3 yếu tố chính từ hóa đơn. Cả 3 yếu tố này đều nằm trong một bảng / chi tiết đơn hàng cho tất cả các hóa đơn.

Các 3 yếu tố là:

  • 1 : Số thuế quan (chữ số)
  • 2 : Số lượng (luôn là một chữ số)
  • 3 : Tổng số lượng dòng (giá trị tiền tệ)

Vui lòng tham khảo ảnh chụp màn hình bên dưới, nơi tôi đã đánh dấu các trường này trên hóa đơn mẫu.

nhập mô tả hình ảnh ở đây

Tôi bắt đầu dự án này với một cách tiếp cận mẫu, dựa trên các biểu thức thông thường . Điều này, tuy nhiên, không thể mở rộng được và tôi đã kết thúc với vô số quy tắc khác nhau.

Tôi hy vọng rằng máy học có thể giúp tôi ở đây - hoặc có thể là một giải pháp lai?

Mẫu số chung

Trong tất cả các hóa đơn của tôi, mặc dù có bố cục khác nhau, mỗi mục hàng sẽ luôn bao gồm một số thuế . Số thuế quan này luôn có 8 chữ số và luôn được định dạng theo một cách như dưới đây:

  • xxxxxxxx
  • xxxx.xxxx
  • xx.xx.xx.xx

(Trong đó "x" là một chữ số từ 0 - 9).

Hơn nữa , như bạn có thể thấy trên hóa đơn có cả Đơn giá và Tổng số tiền trên mỗi dòng. Số tiền tôi sẽ luôn luôn là cao nhất cho mỗi dòng.

Đầu ra

Đối với mỗi hóa đơn như trên, tôi cần đầu ra cho mỗi dòng. Điều này có thể là ví dụ như thế này:

{
    "line":"0",
    "tariff":"85444290",
    "quantity":"3",
    "amount":"258.93"
},
{
    "line":"1",
    "tariff":"85444290",
    "quantity":"4",
    "amount":"548.32"
},
{
    "line":"2",
    "tariff":"76109090",
    "quantity":"5",
    "amount":"412.30"
}

Đi đâu từ đây?

Tôi không chắc chắn về những gì tôi đang tìm kiếm rơi vào học máy và nếu vậy, theo thể loại nào. Có phải là tầm nhìn máy tính? NLP? Được công nhận thực thể?

Suy nghĩ ban đầu của tôi là:

  1. Chuyển đổi hóa đơn thành văn bản. (Các hóa đơn đều ở dạng PDF có thể nhắn tin, vì vậy tôi có thể sử dụng một cái gì đó như pdftotextđể có được các giá trị văn bản chính xác)
  2. Tạo tùy chỉnh tên các tổ chức cho quantity, tariffamount
  3. Xuất các thực thể tìm thấy.

Tuy nhiên, tôi cảm thấy mình có thể đang thiếu thứ gì đó.

Bất cứ ai có thể giúp tôi đi đúng hướng?

Biên tập:

Vui lòng xem bên dưới để biết thêm một số ví dụ về cách phần bảng hóa đơn có thể trông như sau:

Hóa đơn mẫu số 2 nhập mô tả hình ảnh ở đây

Hóa đơn mẫu số 3 nhập mô tả hình ảnh ở đây

Chỉnh sửa 2:

Vui lòng xem bên dưới để biết ba hình ảnh mẫu, không có viền / hộp giới hạn:

Hình 1: Mẫu 1 không có hộp

Hình 2: Mẫu 2 không có hộp

Hình 3: Mẫu 3 không có hộp


Bạn có thể hiển thị thêm một số ví dụ về các tệp PDF đầu vào để xem có bao nhiêu biến thể thực sự không? (= mức độ linh hoạt của giải pháp)
sjaustirni

@sjaustirni Chỉ cần thêm hai! Tôi tin rằng sự khác biệt lớn nhất giữa các hóa đơn của nhà cung cấp là cách bố trí bảng (và sau đó là các chi tiết đơn hàng và cách văn bản cụ thể được định dạng)
oliverbj

Hoàn hảo! Đưa ra các ví dụ này, tôi có thể sẽ chuyển đổi pdf thành văn bản và cố gắng ghép các giá trị trong đó với nhãn trước đó (ví dụ Tariff No.:hoặc $) hoặc cột mà nó thuộc về (ở đây nó có thể giúp bạn lưu thông tin không gian của các chữ cái, nếu bất kỳ công cụ OCR nào làm điều đó). Tôi tin rằng bạn không cần phải học máy với vấn đề này (ngoài OCR được tạo sẵn), cũng không phải NLP (đây không phải là ngôn ngữ tự nhiên). Tuy nhiên, không cần xem các công cụ này hoạt động tốt như thế nào với dữ liệu của bạn, chúng tôi chỉ có thể suy đoán bước tiếp theo là gì và điều gì là cần thiết: D
sjaustirni

@sjaustirni sẽ không kết thúc trong cùng một điều mà tôi đang làm, mà không thể mở rộng? (Cách tiếp cận dựa trên mẫu / regex).
oliverbj

Bạn không thể trích xuất bảng từ pdf sang cơ sở hạ tầng và sau đó xử lý các cột? Có thể bạn có thể sử dụng tabula-py để làm điều này, sau đó lấy số lượng và tổng số trực tiếp, và với một số biểu thức chính thức, thuế quan
dhanushka

Câu trả lời:


4

Tôi đang làm việc với một vấn đề tương tự trong ngành công nghiệp hậu cần và tin tưởng tôi khi tôi nói những bảng tài liệu này có vô số bố cục. Nhiều công ty đã giải quyết được phần nào và đang cải thiện vấn đề này được đề cập như dưới

  • Các nhà lãnh đạo: ABBYY, AntWorks, Kofax và WorkFusion
  • Đối thủ chính: Tự động hóa mọi nơi, Celaton, Datamatics, EdgeVerve, Extract Systems, Hyland, Hyperscience, cơ sở hạ tầng và Parascript
  • Những người khao khát: Ikarus, Rossum, Shipmnts (Alex), Amazon (Textract), Docsumo, Docparser, Aidock

Danh mục tôi muốn đặt vấn đề này sẽ là học đa phương thức , bởi vì cả phương thức văn bản và hình ảnh đều đóng góp rất nhiều cho vấn đề này. Mặc dù mã thông báo OCR đóng vai trò quan trọng trong phân loại giá trị thuộc tính, vị trí của chúng trên trang, khoảng cách và khoảng cách giữa các ký tự giữ các tính năng rất quan trọng trong việc phát hiện ranh giới bảng, hàng và cột. Vấn đề trở nên thú vị hơn khi các hàng vượt qua các trang hoặc một số cột mang các giá trị không trống.

Trong khi thế giới học thuật và hội nghị sử dụng thuật ngữ Xử lý tài liệu thông minh , nói chung để trích xuất cả các trường số ít và dữ liệu dạng bảng. Cái trước được biết đến nhiều hơn bởi phân loại giá trị thuộc tính và cái sau nổi tiếng bởi trích xuất bảng hoặc trích xuất cấu trúc lặp lại, trong tài liệu nghiên cứu.

Trong bước đột phá của chúng tôi trong việc xử lý các tài liệu bán cấu trúc này trong 3 năm, tôi cảm thấy rằng việc đạt được cả độ chính xác và khả năng mở rộng là một hành trình dài và gian khổ. Các giải pháp cung cấp khả năng mở rộng / phương pháp 'mẫu miễn phí' có chú thích các tài liệu kinh doanh bán cấu trúc theo thứ tự hàng chục nghìn, nếu không nói là hàng triệu. Mặc dù phương pháp này là một giải pháp có thể mở rộng, nhưng nó cũng tốt như các tài liệu mà nó đã được đào tạo. Nếu tài liệu của bạn đến từ ngành hậu cần hoặc bảo hiểm, vốn được biết đến với bố cục phức tạp và cần phải cực kỳ chính xác nhờ các quy trình tuân thủ, giải pháp 'dựa trên mẫu' sẽ là liều thuốc cho những người bệnh của bạn. Nó được đảm bảo để cung cấp chính xác hơn.

Nếu bạn cần liên kết đến nghiên cứu hiện tại, hãy đề cập trong các bình luận bên dưới và tôi rất vui lòng chia sẻ chúng.

Ngoài ra, tôi khuyên bạn nên sử dụng pdfparser 1 trên pdf2text hoặc pdfminer vì trước đây cung cấp thông tin cấp độ ký tự trong các tệp kỹ thuật số với hiệu suất tốt hơn đáng kể.

Sẽ rất vui khi kết hợp bất kỳ phản hồi nào, vì đây là câu trả lời đầu tiên của tôi ở đây.


Nếu bạn đang tìm kiếm một repo mã nguồn mở, github.com/invoice-x/invoice2data có thể là một điểm khởi đầu tốt
SIDDHARTH SAHANI

3

Đây là một nỗ lực sử dụng OpenCV, ý tưởng là:

  1. Có được hình ảnh nhị phân. Chúng tôi tải hình ảnh, phóng to bằng cách sử dụng imutils.resizeđể giúp thu được kết quả OCR tốt hơn (xem Tesseract cải thiện chất lượng ), chuyển đổi sang thang độ xám, sau đó ngưỡng của Otsu để có được hình ảnh nhị phân (1 kênh).

  2. Loại bỏ các đường lưới bảng. Chúng tôi tạo ra một hạt nhân ngang và dọc sau đó thực hiện các hoạt động hình thái để kết hợp các đường viền văn bản liền kề thành một đường viền duy nhất. Ý tưởng là trích xuất một hàng ROI thành một phần vào OCR.

  3. Trích xuất ROI hàng. Chúng tôi tìm thấy các đường viền sau đó sắp xếp từ trên xuống dưới bằng cách sử dụng imutils.contours.sort_contours. Điều này đảm bảo rằng chúng tôi lặp qua từng hàng theo đúng thứ tự. Từ đây, chúng tôi lặp lại qua các đường viền, trích xuất ROI hàng bằng cách sử dụng Numpy lát, OCR bằng Pytesseract , sau đó phân tích dữ liệu.


Đây là hình dung của từng bước:

Hình ảnh đầu vào

nhập mô tả hình ảnh ở đây

Hình ảnh nhị phân

nhập mô tả hình ảnh ở đây

Đóng hình thái

nhập mô tả hình ảnh ở đây

Hình dung của việc lặp qua từng hàng

nhập mô tả hình ảnh ở đây

Trích xuất ROI hàng

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Kết quả dữ liệu hóa đơn đầu ra:

{'line': '0', 'tariff': '85444290', 'quantity': '3', 'amount': '258.93'}
{'line': '1', 'tariff': '85444290', 'quantity': '4', 'amount': '548.32'}
{'line': '2', 'tariff': '76109090', 'quantity': '5', 'amount': '412.30'}

Thật không may, tôi nhận được kết quả hỗn hợp khi thử trên hình ảnh thứ 2 và thứ 3. Phương pháp này không tạo ra kết quả tuyệt vời trên các hình ảnh khác vì bố cục của các hóa đơn đều khác nhau. Tuy nhiên, phương pháp này cho thấy có thể sử dụng các kỹ thuật xử lý hình ảnh truyền thống để trích xuất thông tin hóa đơn với giả định rằng bạn có bố cục hóa đơn cố định.

import cv2
import numpy as np
import pytesseract
from imutils import contours
import imutils

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

# Load image, enlarge, convert to grayscale, Otsu's threshold
image = cv2.imread('1.png')
image = imutils.resize(image, width=1000)
height, width = image.shape[:2]
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, (50,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(thresh, [c], -1, 0, -1)

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,50))
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(thresh, [c], -1, 0, -1)

# Morph close to combine adjacent contours into a single contour
invoice_data = []
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (85,5))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=3)

# Find contours, sort from top-to-bottom
# Iterate through contours, extract row ROI, OCR, and parse data
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
(cnts, _) = contours.sort_contours(cnts, method="top-to-bottom")

row = 0
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    ROI = image[y:y+h, 0:width]
    ROI = cv2.GaussianBlur(ROI, (3,3), 0)
    data = pytesseract.image_to_string(ROI, lang='eng', config='--psm 6')
    parsed = [word.lower() for word in data.split()] 
    if 'tariff' in parsed or 'number' in parsed:
        row_data = {}
        row_data['line'] = str(row)
        row_data['tariff'] = parsed[-1]
        row_data['quantity'] = parsed[2]
        row_data['amount'] = str(max(parsed[10], parsed[11]))
        row += 1

        print(row_data)
        invoice_data.append(row_data)

        # Visualize row extraction
        '''
        mask = np.zeros(image.shape, dtype=np.uint8)
        cv2.rectangle(mask, (0, y), (width, y + h), (255,255,255), -1)
        display_row = cv2.bitwise_and(image, mask)

        cv2.imshow('ROI', ROI)
        cv2.imshow('display_row', display_row)
        cv2.waitKey(1000)
        '''
print(invoice_data)
cv2.imshow('thresh', thresh)
cv2.imshow('close', close)
cv2.waitKey()

3
Cảm ơn bạn @nathancy! Mặc dù câu trả lời này không phải là một giải pháp chung cho tất cả các hóa đơn được đăng của tôi, tôi vẫn nghĩ rằng đó là lần gần nhất tôi thấy bất kỳ ai đến - và đây chỉ là sử dụng OpenCV. Rất tuyệt và ví dụ mã của bạn đã dạy tôi rất nhiều! Một lần nữa, cảm ơn bạn đã dành thời gian đăng bài này.
oliverbj
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.