Перейти к содержимому
Compvision.ru
Борода22

Распознавание реквизитов платежного документа

Recommended Posts

Приветствую, ребята. Направьте пожалуйста в нужное русло. Задача у меня такая. Распознать реквизиты на платежном документе. Есть скан платежного документа. Т.к. это унифицированная форма документа, то каждый реквизит находится на определенном месте. Подскажите пожалуйста, можно ли как-то извлечь данные по определенным координатам и распознать текст ? Использую OpenCvSharp4. В интернетах находил код (при необходимости могу выложить) и общее описание алгоритма, но совсем не понимаю что, куда и откуда. Буду признателен если кто-то проведет ликбез.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Предлагаю начать либо с голого tesseract-ocr, либо с примеров из opencv_text. А потом по результатам.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

У меня есть такой код на шарпе:

        public void RunTextRecog()
        {
            List<Rect> boundRect = new List<Rect>();

            using (Mat img = new Mat(PathToImage))
            using (Mat img_gray = new Mat())
            using (Mat img_sobel = new Mat())
            
            using (Mat img_threshold = new Mat())
            {
                Cv2.CvtColor(img, img_gray, ColorConversionCodes.BGR2GRAY);
                Cv2.Sobel(img_gray, img_sobel, MatType.CV_8U, 1, 0, 3, 1, 0, BorderTypes.Default);
                //Cv2.AdaptiveThreshold(img_sobel, img_threshold, 250, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 3, 1);
                Cv2.Threshold(img_sobel, img_threshold, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);
                using (Mat element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(20, 20)))
                {
                    Cv2.MorphologyEx(img_threshold, img_threshold, MorphTypes.Close, element);
                    Point[][] edgesArray = img_threshold.Clone().FindContoursAsArray(RetrievalModes.External, ContourApproximationModes.ApproxNone);
                    foreach (Point[] edges in edgesArray)
                    {
                        Point[] normalizedEdges = Cv2.ApproxPolyDP(edges, 17, true);
                        Rect appRect = Cv2.BoundingRect(normalizedEdges);
                        boundRect.Add(appRect);
                    }
                }

                for (int ind = 0; ind < boundRect.Count; ind++)
                {
                    Cv2.Rectangle(img,
                        new Point(boundRect[ind].X, boundRect[ind].Y),
                        new Point(boundRect[ind].X + boundRect[ind].Width, boundRect[ind].Y + boundRect[ind].Height),
                        new Scalar(100, 200, 0),
                        3);
                }
                Mat resize_img = new Mat();
                Cv2.ImShow("Результат", img);
                Cv2.ImShow("Собель", img_sobel);
                Cv2.ImShow("Threshold", img_threshold);
                Cv2.ImShow("Grey", img_gray);
                Cv2.ImWrite("segmented.jpg", img);
            }
        }

Результаты на скриншотах. Как я понял опытным путем (изменяя параметры), выделение объектов происходит с помощью GetStructuringElement().

original.png

sobel.png

threshold.png

result.png

У меня тут еще идея возникла. Тупо вырезать определенные области на сканере, и скармливать их tesseract-у

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Прислушайтесь к тому, что советует Nuzhny, начните с tesseract-ocr, есть даже бинды под шарп https://github.com/charlesw/tesseract например. 

Пример применения : https://github.com/charlesw/tesseract-samples

Но если есть много энергии и желание повозиться, пишите, посоветуем по шагам, но это длинный путь )

 

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@Smorodov Спасибо большое. Я уже пробовал использовать tesseract sdk, распознает кириллицу, но как я понял, распознавание идет "потоком". Т.е. распознает все, но в одну строку. Полагаю что разбор такой строки будет еще тот геморрой. Либо я подключал не ту библиотеку, либо что-то не так настроил, но tesseract дает распознать картинку максимальным разрешением в 500 px.

Как я понимаю, у меня два пути - поиск областей с данными с помощью OpenCVSharp и потом скармливать эти области tesseract-у, либо определиться с координатами фиксированных областей, и потом опять же, скармливать их tesseract-у для распознавания. Кто что может подсказать :) ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Установил из Nu-get`а библиотеку Tesseract 4.1. А дальше что ? Как я понял, в проект еще необходимо добавить какие-то библиотеки ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

В OpenCv ecть встроенные детекторы текста, гляньте здесь: https://www.pyimagesearch.com/2018/08/20/opencv-text-detection-east-text-detector/

 

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

И вот столкнулся с проблемой. Не хочет распознавать одиночные символы. Например, вот как на скрине. Что можно попытаться предпринять ? Увеличить изображение ? Причем, если "дописать" цифру слева или справа, то распознает корректно.

cropped_image.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Вот, кстати:

https://www.sk-spell.sk.cx/tesseract-ocr-parameters-in-302-version

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Я использую вот такой код:

var img = Pix.LoadFromFile(pathToImage);
var page = tessEngine.Process(img);
string result = page.GetText();
page.Dispose();

 

47 минут назад, Smorodov сказал:

попробуйте увеличить размер изображения

Для этого нужно использовать метод Scale() ?

Как я понял, нужно для page segmentation mode установить режим single char. Как это сделать ?

Установил свойство DefaultPageSegMode = PageSegMode.SingleChar, теперь распознает, но не корректно распознает строки :lol:

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

На CPP это cv::resize , на шарпе не знаю.

Ну если все получилось, поздравляю!  ))  

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
15 часов назад, Smorodov сказал:

Ну если все получилось, поздравляю!  ))  

Да, кажется все нормально, спасибо большое!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Как корректно распознать области(границы) с текстом ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

На английском конечно, но по теме ) : https://www.pyimagesearch.com/2014/09/01/build-kick-ass-mobile-document-scanner-just-5-minutes/

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

https://ru.wikipedia.org/wiki/Математическая_морфология

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
12 часа назад, Smorodov сказал:

На английском конечно, но по теме )

Это просто жуть, тут не то что бутылка нужна, нужно заново родиться, и впитывать это с молоком матери:lol:

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

dilate - утолщит все с сольет в блоки. Структурный элемент взять высотой с символ, шириной примерно в 1.5 - 2.5 от ширины символа.

после него erode с тем же ядром, чтобы вернуть размеры блоков. 

Повторить поиск контуров. 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@Smorodov, Приветствую, уважаемый! А можно пример в виде используемых функций ? Простите уж за такое наглое пожелание :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Все-таки видимо нужно идти другим путем. Убрать все прямые вертикальные и горизонтальные линии, т.к. они мешают для утолщения.

Написал (поправил чужой код) вот такой код для поиска и отображения прямых линий:

Mat original_img = new Mat(PathToImage);
Mat gray_img = original_img.CvtColor(ColorConversionCodes.BGR2GRAY);
Mat thresh_img = gray_img.Threshold(0, 255, ThresholdTypes.Otsu | ThresholdTypes.BinaryInv);

var lines = Cv2.HoughLines(thresh_img, 200, Math.PI/180, 2);

for(int x = 0; x < lines.Count(); x++)
{
   float rho = lines[x].Rho;
   float theta = lines[x].Theta;

   double a = Math.Cos(theta);
   double b = Math.Sin(theta);
   double x0 = a * rho;
   double y0 = b * rho;

   LineSegmentPoint point = new LineSegmentPoint();

   point.P1.X = (int)Math.Round(x0 + (1000 * (-b)));
   point.P1.Y = (int)Math.Round(y0 + (1000 * (a)));

   point.P2.X = (int)Math.Round(x0 - (1000 * (-b)));
   point.P2.Y = (int)Math.Round(y0 - (1000 * (a)));

   Cv2.Line(original_img, point.P1, point.P2, new Scalar(0, 200, 20));
}

Но линии рисуются вообще не правильно (рисуются диагоналями, под углом), я так понимаю, что накосячил где-то в преобразовании координат ?

И что это за магические числа в виде 1000 ?

lines.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Извиняюсь что запоздал с ответом, был в отъезде. 

Сишный пример из стандартного набора:

 

#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
    Mat src, dst, color_dst;
    if( argc != 2 || !(src=imread(argv[1], 0)).data)
        return -1;

    Canny( src, dst, 50, 200, 3 );
    cvtColor( dst, color_dst, COLOR_GRAY2BGR );

    vector<Vec4i> lines;
    HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 );
    for( size_t i = 0; i < lines.size(); i++ )
    {
        line( color_dst, Point(lines[i][0], lines[i][1]),
        Point( lines[i][2], lines[i][3]), Scalar(0,0,255), 3, 8 );
    }
    namedWindow( "Source", 1 );
    imshow( "Source", src );

    namedWindow( "Detected Lines", 1 );
    imshow( "Detected Lines", color_dst );

    waitKey(0);
    return 0;
}

  По поводу морфологических операций, cv::dilate затем cv::erode. 

Для детектора линий, нужно белое изображение на черном фоне. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×