Автоматизация распознавания и подсчёта транзакций с изображений
- вторник, 27 августа 2024 г. в 00:00:09
Большинство процессов в нашем современном мире стремится к автоматизации. Хотелось бы разместить здесь свою наработку. Надеюсь данный материал найдёт своего читателя. В данной статье рассмотрим автоматизацию введения ежедневных отчетов компании.
Избавление сотрудника от рутинной задачи.
Минимизация ошибок, которые мог бы случайно допустить человек.
Оптимизация затрат компании, сократить расходы на содержание штата сотрудников: менеджеров и бухгалтеров.
В данной статье я хочу поделиться, как можно автоматизировать процессы внутри компании с помощью моего скрипта.
Архитектуру, которую вы видите на рисунке 1, очень примитивная. Данный скрипт находится в режиме разработки, сейчас он хорошо обрабатывает изображения с транзакциями Сбербанк.
Для работы с изображением я использовал следующие библиотеки:
Tesseract – это библиотека JavaScript, которая извлекает из изображений слова практически на любом языке.
Sharp – это библиотека JavaScript, которая обрабатывает фото, на примере увидим, что она конкретно выполняет.
Дополнительно: Для вывода логов я использовал библиотеку Pino.
Обработка изображения.
Распознавание текста на изображении с помощью OCR.
Извлечение и подсчёт финансовых данных.
Давайте посмотрим файлы в директории utils
.
import pino from 'pino';
import pretty from 'pino-pretty';
export const logger = pino(pretty({
colorize: true,
levelFirst: true,
}));
Pino предоставляет дополнительную функциональность и гибкость + в некоторых проектах линтеры могут запрещать использование console.log
.
import { fileURLToPath } from 'url';
import path, { dirname as pathDirname, resolve } from 'path';
import sharp from 'sharp';
const filename = fileURLToPath(import.meta.url);
const dirname = pathDirname(filename);
export const getPath = (relativePath) => {
const ROOT_PATH = path.join(dirname, '../');
return resolve(ROOT_PATH, relativePath);
};
export const preload = async (input, output) => {
return await sharp(input)
.resize(1200)
.grayscale()
.normalize()
.sharpen()
.toBuffer();
};
В этом коде мы используем библиотеку sharp
, которая предназначена для обработки изображений в Node.js.
Что мы здесь делаем?
Изменяем размер изображения, делаем картинку черно-белой, улучаем контрастность, повышаем резкость => сохраняем результат в output.
Для чего мы это делаем?
Улучшаем визуальное качество нашего изображения. Тестирование показало, что не все транзакции распознает Tesseract.
Рассмотрим главный файл app.js
.
import Tesseract from 'tesseract.js';
import { preload, getPath } from './utils/preload.js';
import { logger } from './utils/logger.js';
import { keywords } from './keywords.js';
const input = getPath('./assets/input.jpg');
const run = async (input) => {
const output = await preload(input);
try {
const { data: { text } } = await Tesseract.recognize(output, 'rus', {
logger: info => logger.info(info.status)
});
const amounts = [];
const priceWithRRegex = /[+-]?\d+(?:[\s,]\d{3})*(?:[.,]\d+)?\s*Р/g;
let ignoreNext = false;
text.split('\n').forEach(line => {
if (keywords.some(keyword => line.includes(keyword))) {
ignoreNext = true;
}
if (!ignoreNext) {
const priceMatches = line.match(priceWithRRegex);
if (priceMatches) {
priceMatches.forEach(priceMatch => {
let price = priceMatch
.replace(/\s/g, '')
.replace(',', '.')
.replace('Р', '')
.trim();
if (!price.startsWith('+') && !price.startsWith('-')) {
price = '-' + price;
}
const numericPrice = parseFloat(price);
if (!isNaN(numericPrice)) {
amounts.push(numericPrice);
}
});
}
}
if (ignoreNext) {
ignoreNext = false;
}
});
const total = amounts.reduce((acc, curr) => acc + curr, 0);
return {
amounts,
total
};
} catch (error) {
logger.info(error);
throw error;
}
};
run(input).then(result => {
logger.info(result, 'Result: ');
});
Сначала вызывается функция preload
, которая выполняет обработку изображения. Как я писал раннее, это нам нужно для того, чтобы улучшить качество распознавания текста.
Обрабатываем изображение с помощью Tesseract. Текст, который мы получили, разбивается на строки. Используя регулярное выражение priceWithRRegex
, мы ищем в каждой строке суммы, обозначенные символом "Р". Также я задал keywords
, после этих ключевых слов сумма не входит в массив. Например, общая сумма за день.
В итоге, после обработки данных мы получаем массив транзакций и их общую сумму.
Код всего проекта на GitHub.
Давайте на примере данного изображения рассмотрим, как работает скрипт.
Изображение, которое мы будем обрабатывать:
Обработанное изображение:
После обработки изображения идет извлечение текста и получение всех транзакций. В итоге мы получаем массив транзакций и их общую сумму.
Данный пример хорошо показывает, как с помощью такого скрипта автоматизировать процесс ведения ежедневного отчёта. Он решает проблемы, описанные в статье, и упрощает работу с финансовыми данными. Данный скрипт находится в этапе разработки, было бы хорошо его дорабатывать вместе с другими разработчиками. В ближайших планах — добавить поддержку распознавания транзакций с других банков, которые используют несколько отличающийся формат данных. В будущем можно интегрировать этот скрипт с другими системами, такими как МойСклад, что позволит автоматизировать ещё больший объём работы, включая синхронизацию данных с бухгалтерскими системами, управление складскими остатками и улучшение общего управления бизнесом.