В этом руководстве вы узнаете, как проанализировать изображение квитанции с помощью Python. Это может помочь вам автоматически извлекать данные из квитанций без вмешательства человека и использовать их в ваших внутренних приложениях для многих целей бухгалтерского учета.

Последний скрипт можно использовать в любом приложении промежуточного слоя на Python или в REST API, обслуживающем ваши внешние интерфейсы.

В конце этого руководства мы покажем вам, как выделить извлеченные поля квитанции на изображении с помощью библиотеки компьютерного зрения с открытым исходным кодом OpenCV.

Окончательный результат выглядит так:

Предварительные требования к API

  1. В вашей среде должен быть установлен python. В этом руководстве мы используем версию 3.7.
  2. Вам понадобится бесплатная учетная запись Mindee. Зарегистрируйтесь и подтвердите свой адрес электронной почты для входа.
  3. Квитанция. Поищите в сумке / кошельке последнюю квитанцию ​​или выполните поиск изображений в Google, чтобы найти квитанцию, и загрузите несколько для проверки.

Настроить проект

Создайте пустой каталог в любом месте портативного компьютера, мы назовем его «python_receipt» и создадим в нем файл «main.py».

Откройте терминал и перейдите в только что созданный каталог. В этом руководстве мы будем использовать «запросы» библиотеки Python для вызова API. Если он не установлен в вашем окружении, вы можете установить его, выполнив:

pip install requests

Убедитесь, что на вашем ноутбуке есть изображение квитанции и вы знаете путь.

На этом настройка, вызовем API.

Вызов API получения Mindee

Войдите на platform.mindee.net и войдите в свою среду API квитанции о расходах, щелкнув следующую карточку.

Если у вас еще нет токена API для этого API, перейдите в раздел «учетные данные» и создайте новый ключ API.

Щелкните раздел документации на панели навигации, а затем щелкните ссылку Python в области образца кода. Скопируйте код.

Откройте файл main.py, который вы создали несколько минут назад, и вставьте образец кода, он должен выглядеть так:

import requests 
url = "https://api.mindee.net/products/expense_receipts/v2/predict" 
with open("/path/to/my/file", "rb") as myfile: 
    files = {"file": myfile} 
    headers = {"X-Inferuser-Token": "my-token-here"} 
    response = requests.post(url, files=files, headers=headers) 
    print(response.text)

Замените заполнитель «путь / к / моему / файлу» в коде на путь к изображению квитанции, из которого вы хотите извлечь данные.

Замените my-token-here на токен API, который вы создали ранее на платформе.

Вы можете вернуться к своей консоли и запустить скрипт

python main.py

Вы должны увидеть в консоли объект json с результатами, извлеченными из API синтаксического анализа квитанции. Теперь давайте проанализируем этот json, чтобы получить нужные нам данные.

Разобрать результат

API извлекает список различных полей в квитанции (общая сумма, налоги, дата…). Вы можете найти описание всех полей во вкладке «документация» на странице платформы Mindee API.

Теперь, когда мы закодировали запрос API, мы воспользуемся json-объектом и получим несколько извлеченных функций.

Во-первых, давайте создадим функцию синтаксического анализа, которая возвращает общую сумму квитанции, дату и час платежа, а также категорию расходов. В учебнике мы сосредоточимся только на этих четырех функциях.

Функции находятся внутри объекта прогноза в ответе json. Поскольку вы можете отправить многостраничный PDF-файл в API, объект прогнозов представляет собой массив с объектом прогноза для каждой страницы.

В нашем случае, поскольку мы отправляем простое изображение в формате jpg, массив прогнозов содержит только один объект.

def get_features(json_response):
    parsed_data = {}
    prediction = json_response["predictions"][0]
    parsed_data["total_amount"] = prediction["total"]["amount"]
    parsed_data["time"] = prediction["time"]["iso"]
    parsed_data["date"] = prediction["date"]["iso"]
    parsed_data["category"] = prediction["category"]["value"]
    return parsed_data

Теперь, когда наша функция готова, мы просто напечатаем результат в консоли и добавим простую проверку, чтобы убедиться, что вызов API был выполнен правильно, и мы можем получить доступ к результату. Вы можете заменить скрипт main.py на это:

Теперь, когда наша функция готова, мы просто напечатаем результат в консоли и добавим простую проверку, чтобы убедиться, что вызов API был выполнен правильно, и мы можем получить доступ к результату.

Вы можете заменить скрипт main.py на это:

import requests

def get_features(json_response):
    parsed_data = {}
    prediction = json_response["predictions"][0]
    parsed_data["total_amount"] = prediction["total"]["amount"]
    parsed_data["time"] = prediction["time"]["iso"]
    parsed_data["date"] = prediction["date"]["iso"]
    parsed_data["category"] = prediction["category"]["value"]
    return parsed_data

url = "https://api.mindee.net/products/expense_receipts/v2/predict"
with open("/path/to/my/file", "rb") as myfile:
    files = {"file": myfile}
    headers = {"X-Inferuser-Token": "my-token-here"}
    response = requests.post(url, files=files, headers=headers)
    if response.status_code != 200:
        print("Request error")
    else:
        json_response = response.json()
        features = get_features(json_response)
        print("Date:", features["date"])
        print("Time:", features["time"])
        print("Total amount:", features["total_amount"])
        print("Category:", features["category"])

Запустите сценарий и проверьте результаты на своем изображении.

Наконец, мы собираемся добавить последний фрагмент кода, который выделит функции, извлеченные на изображении. Это может помочь вам или вашим пользователям очень быстро проверить извлечение данных.

Выделите особенности изображения

Давайте попробуем выделить функции, как если бы кто-то делал это ручкой.

Во-первых, вам необходимо установить библиотеку Python для компьютерного зрения OpenCV, если она еще не установлена ​​в вашем окружении. Для этого запустите:

pip install opencv-python

Нам нужно немного изменить нашу функцию get_features, чтобы она возвращала координаты для каждого объекта, поскольку мы используем это, чтобы знать, где рисовать прямоугольники.

def get_features(json_response):
    parsed_data = {}
    coordinates = []
    prediction = json_response["predictions"][0]
    parsed_data["total_amount"] = prediction["total"]["amount"]
    parsed_data["time"] = prediction["time"]["iso"]
    parsed_data["date"] = prediction["date"]["iso"]
    parsed_data["category"] = prediction["category"]["value"]
    coordinates.append(prediction["total"]["segmentation"]["bounding_box"])
    coordinates.append(prediction["time"]["segmentation"]["bounding_box"])
    coordinates.append(prediction["date"]["segmentation"]["bounding_box"])
    return parsed_data, coordinates

Как только это будет сделано, мы собираемся создать функцию выделения в несколько этапов, а именно:

  1. Открыть изображение с пути
  2. Создайте маску изображения
  3. Зацикливайтесь на координатах каждого объекта и нарисуйте прямоугольник объекта на нашей маске.
  4. Наложение маски и исходного изображения с альфа-каналом
  5. Показать изображение пользователю

Примечание: каждая координата, возвращаемая API, является относительной (в% от изображения). Вы увидите, что в коде есть относительное преобразование в абсолютное.

Вот пошаговый код:

def highlight_features(img_path, coordinates):
    # step 1: Open the image from path
    cv_image = cv2.imread(img_path)
    # step 2: create mask image
    overlay = cv_image.copy()
    h, w = cv_image.shape[:2]
    # step 3: Loop on each feature coordinates and draw the feature rectangle on our mask
    for coord in coordinates:
        pt1 = (int(w*coord[0][0]), int(h*coord[0][1]))
        pt2 = (int(w*coord[2][0]), int(h*coord[2][1]))
        cv2.rectangle(overlay, pt1, pt2, (70, 230, 244), cv2.FILLED)
    # step 4: Overlay the mask and original image with alpha
    final_image = cv2.addWeighted(overlay, 0.5, cv_image, 0.5, 0)
    # step 5: Display image to the user
    cv2.imshow("highlghted_image", cv2.resize(final_image, (400, int(400*h/w))))
    cv2.waitKey(0)

Наконец, нам просто нужно немного изменить код для выполнения функции выделения и печати результатов. Вот как выглядит окончательный код:

import requests
import cv2
def get_features(json_response):
    parsed_data = {}
    coordinates = []
    prediction = json_response["predictions"][0]
    parsed_data["total_amount"] = prediction["total"]["amount"]
    parsed_data["time"] = prediction["time"]["iso"]
    parsed_data["date"] = prediction["date"]["iso"]
    parsed_data["category"] = prediction["category"]["value"]
    coordinates.append(prediction["total"]["segmentation"]["bounding_box"])
    coordinates.append(prediction["time"]["segmentation"]["bounding_box"])
    coordinates.append(prediction["date"]["segmentation"]["bounding_box"])
    return parsed_data, coordinates

def highlight_features(img_path, coordinates):
    # step 1: Open the image from path
    cv_image = cv2.imread(img_path)
    # step 2: create mask image
    overlay = cv_image.copy()
    h, w = cv_image.shape[:2]
    # step 3: Loop on each feature coordinates and draw the feature rectangle on our mask
    for coord in coordinates:
        pt1 = (int(w*coord[0][0]), int(h*coord[0][1]))
        pt2 = (int(w*coord[2][0]), int(h*coord[2][1]))
        cv2.rectangle(overlay, pt1, pt2, (70, 230, 244), cv2.FILLED)
    # step 4: Overlay the mask and original image with alpha
    final_image = cv2.addWeighted(overlay, 0.5, cv_image, 0.5, 0)
    # step 5: Display image to the user
    cv2.imshow("highlghted_image", cv2.resize(final_image, (400, int(400*h/w))))
    cv2.waitKey(0)

url = "https://api.mindee.net/products/expense_receipts/v2/predict"
with open("/path/to/my/file", "rb") as myfile:
    files = {"file": myfile}
    headers = {"X-Inferuser-Token": "my-token-here"}
    response = requests.post(url, files=files, headers=headers)
    if response.status_code != 200:
        print("Request error")
    else:
        json_response = response.json()
        features, coords = get_features(json_response)
        print("Date:", features["date"])
        print("Time:", features["time"])
        print("Total amount:", features["total_amount"])
        print("Category:", features["category"])
        highlight_features("path/to/my/file", coords)

В последней строке кода не забудьте заменить заполнитель «путь / к / моему / файлу» на текущий путь к изображению.

И конечный результат!

Заключение

Менее чем за 1 секунду была загружена квитанция, API извлек данные квитанции, затем результат был проанализирован и выделен на изображении. Круто, правда?

Если вы хотите использовать этот тип сценария для отображения результатов для ваших пользователей, я бы посоветовал вам сделать выделение во внешнем приложении, поскольку отправка изображений обратно из вашего промежуточного программного обеспечения - не лучший вариант из-за размеров полезной нагрузки. Другое решение - сохранить окончательное изображение с помощью cv2.imwrite (…), но при этом ваш клиент загрузит результат. Чтобы помочь вам с рендерингом ваших изображений на стороне клиента, вы можете воспользоваться нашим ReactJS SDK с открытым исходным кодом.

Если у вас есть вопросы, обращайтесь к нам!