Обработка изображений для повышения точности распознавания текста

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

Какие методы обработки изображений могут улучшить точность? Я использовал размытие по Гауссу, чтобы сгладить пиксельные изображения и увидел небольшое улучшение, но я надеюсь, что есть более специфическая техника, которая даст лучшие результаты. Скажем, фильтр, настроенный на черно-белые изображения, который сгладил бы неровные края, а затем фильтр, который увеличил бы контраст, чтобы сделать символы более четкими.

Какие-нибудь общие советы для тех, кто является новичком в обработке изображений?

14 ответов

Решение
  1. исправить DPI (при необходимости) минимум 300 DPI
  2. исправить размер текста (например, 12 пунктов должно быть в порядке)
  3. попытаться исправить текстовые строки (выравнивание и удаление текста)
  4. попытаться исправить освещение изображения (например, нет темной части изображения)
  5. бинаризация и устранение шума изображения

Не существует универсальной командной строки, которая бы подходила для всех случаев (иногда нужно размыть и повысить резкость изображения). Но вы можете попробовать TEXTCLEANER из скриптов ImageMagick Фреда.

Если вы не являетесь поклонником командной строки, возможно, вы можете попробовать использовать opensource http://scantailor.sourceforge.net/ или коммерческий бухгалтер.

Я ни в коем случае не эксперт OCR. Но мне на этой неделе пришлось конвертировать текст из jpg.

Я начал с цветного, RGB 445x747 пикселей jpg. Я сразу попробовал тессеракт на этом, и программа практически ничего не конвертировала. Затем я вошел в GIMP и сделал следующее. image>mode>grayscale image>scale image>1191x2000 пикселей фильтры> улучшение> нерезкая маска со значениями радиуса = 6,8, количество = 2,69, порог = 0 Затем я сохраняю как новый jpg с качеством 100%.

Затем Tesseract смог извлечь весь текст в файл.txt

Гимп твой друг.

Как правило, я использую следующие методы предварительной обработки изображений с использованием библиотеки OpenCV:

  1. Изменение масштаба изображения (рекомендуется, если вы работаете с изображениями, которые имеют разрешение менее 300 dpi):

    img = cv2.resize(img, None, fx=1.2, fy=1.2, interpolation=cv2.INTER_CUBIC)
    
  2. Преобразование изображения в оттенки серого:

    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
  3. Применение расширения и эрозии для устранения шума (вы можете поиграть с размером ядра в зависимости от набора данных):

    kernel = np.ones((1, 1), np.uint8)
    img = cv2.dilate(img, kernel, iterations=1)
    img = cv2.erode(img, kernel, iterations=1)
    
  4. Применение размытия, которое можно выполнить с помощью одной из следующих строк (однако каждая из них имеет свои плюсы и минусы, однако медианное размытие и двусторонний фильтр обычно работают лучше, чем размытие по Гауссу.):

    cv2.threshold(cv2.GaussianBlur(img, (5, 5), 0), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.threshold(cv2.bilateralFilter(img, 5, 75, 75), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.threshold(cv2.medianBlur(img, 3), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.adaptiveThreshold(cv2.GaussianBlur(img, (5, 5), 0), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    
    cv2.adaptiveThreshold(cv2.bilateralFilter(img, 9, 75, 75), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    
    cv2.adaptiveThreshold(cv2.medianBlur(img, 3), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    

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

Если вы хотите проверить их, я делюсь ссылками с вами:

Три пункта для улучшения читабельности изображения: 1) Измените размер изображения с переменной высотой и шириной (умножьте 0,5 и 1 и 2 на высоту и ширину изображения). 2) Преобразуйте изображение в формат оттенков серого (черно-белый). 3) Уберите пиксели шума и сделайте их более четкими (Фильтруйте изображение).

Смотрите ниже код:

//Resize
  public Bitmap Resize(Bitmap bmp, int newWidth, int newHeight)
        {

                Bitmap temp = (Bitmap)bmp;

                Bitmap bmap = new Bitmap(newWidth, newHeight, temp.PixelFormat);

                double nWidthFactor = (double)temp.Width / (double)newWidth;
                double nHeightFactor = (double)temp.Height / (double)newHeight;

                double fx, fy, nx, ny;
                int cx, cy, fr_x, fr_y;
                Color color1 = new Color();
                Color color2 = new Color();
                Color color3 = new Color();
                Color color4 = new Color();
                byte nRed, nGreen, nBlue;

                byte bp1, bp2;

                for (int x = 0; x < bmap.Width; ++x)
                {
                    for (int y = 0; y < bmap.Height; ++y)
                    {

                        fr_x = (int)Math.Floor(x * nWidthFactor);
                        fr_y = (int)Math.Floor(y * nHeightFactor);
                        cx = fr_x + 1;
                        if (cx >= temp.Width) cx = fr_x;
                        cy = fr_y + 1;
                        if (cy >= temp.Height) cy = fr_y;
                        fx = x * nWidthFactor - fr_x;
                        fy = y * nHeightFactor - fr_y;
                        nx = 1.0 - fx;
                        ny = 1.0 - fy;

                        color1 = temp.GetPixel(fr_x, fr_y);
                        color2 = temp.GetPixel(cx, fr_y);
                        color3 = temp.GetPixel(fr_x, cy);
                        color4 = temp.GetPixel(cx, cy);

                        // Blue
                        bp1 = (byte)(nx * color1.B + fx * color2.B);

                        bp2 = (byte)(nx * color3.B + fx * color4.B);

                        nBlue = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        // Green
                        bp1 = (byte)(nx * color1.G + fx * color2.G);

                        bp2 = (byte)(nx * color3.G + fx * color4.G);

                        nGreen = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        // Red
                        bp1 = (byte)(nx * color1.R + fx * color2.R);

                        bp2 = (byte)(nx * color3.R + fx * color4.R);

                        nRed = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        bmap.SetPixel(x, y, System.Drawing.Color.FromArgb
                (255, nRed, nGreen, nBlue));
                    }
                }



                bmap = SetGrayscale(bmap);
                bmap = RemoveNoise(bmap);

                return bmap;

        }


//SetGrayscale
  public Bitmap SetGrayscale(Bitmap img)
        {

            Bitmap temp = (Bitmap)img;
            Bitmap bmap = (Bitmap)temp.Clone();
            Color c;
            for (int i = 0; i < bmap.Width; i++)
            {
                for (int j = 0; j < bmap.Height; j++)
                {
                    c = bmap.GetPixel(i, j);
                    byte gray = (byte)(.299 * c.R + .587 * c.G + .114 * c.B);

                    bmap.SetPixel(i, j, Color.FromArgb(gray, gray, gray));
                }
            }
            return (Bitmap)bmap.Clone();

        }
//RemoveNoise
   public Bitmap RemoveNoise(Bitmap bmap)
        {

            for (var x = 0; x < bmap.Width; x++)
            {
                for (var y = 0; y < bmap.Height; y++)
                {
                    var pixel = bmap.GetPixel(x, y);
                    if (pixel.R < 162 && pixel.G < 162 && pixel.B < 162)
                        bmap.SetPixel(x, y, Color.Black);
                    else if (pixel.R > 162 && pixel.G > 162 && pixel.B > 162)
                        bmap.SetPixel(x, y, Color.White);
                }
            }

            return bmap;
        }

ВХОДНОЕ ИЗОБРАЖЕНИЕ
ВХОДНОЕ ИЗОБРАЖЕНИЕ

ВЫХОДНОЕ ИЗОБРАЖЕНИЕВЫХОДНОЕ ИЗОБРАЖЕНИЕ

Что мне ЧЕГО ПОМОГЛО на этом пути, так это исходные коды проекта Capture2Text. http://sourceforge.net/projects/capture2text/files/Capture2Text/.

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

Обратите особое внимание на файл Capture2Text\SourceCode\leptonica_util\leptonica_util.c - в этом суть предварительной обработки изображения для этой утилиты.

Если вы запустите двоичные файлы, вы можете проверить преобразование изображения до / после процесса в папке Capture2Text\Output\.

PS упомянутое решение использует Tesseract для OCR и Leptonica для предварительной обработки.

Это несколько лет назад, но все еще может быть полезным.

Мой опыт показывает, что иногда помогает изменение размера изображения в памяти перед передачей его в tesseract.

Попробуйте разные режимы интерполяции. Пост /questions/18035736/kak-izmenit-razmer-buferizovannogo-izobrazheniya-i-grafiki-2d-v-java/18035752#18035752 мне очень помог.

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

В некоторой степени Тессеракт автоматически применяет их. Также можно сказать Тессеракту написать промежуточное изображение для проверки, т.е. проверить, насколько хорошо работает внутренняя обработка изображения (поиск tessedit_write_images в приведенной выше ссылке).

Что еще более важно, новая нейросетевая система в Tesseract 4 дает гораздо лучшие результаты распознавания текста - в целом и особенно для изображений с некоторым шумом. Это включено с --oem 1например, как в:

$ tesseract --oem 1 -l deu page.png result pdf

(этот пример выбирает немецкий язык)

Таким образом, имеет смысл сначала проверить, насколько далеко вы продвинулись в новом режиме Tesseract LSTM, прежде чем применять некоторые пользовательские этапы предварительной обработки изображений.

(по состоянию на конец 2017 года Tesseract 4 еще не выпущен как стабильный, но версия для разработки пригодна для использования)

Java-версия для кода Сатьяраджа выше:

// Resize
public Bitmap resize(Bitmap img, int newWidth, int newHeight) {
    Bitmap bmap = img.copy(img.getConfig(), true);

    double nWidthFactor = (double) img.getWidth() / (double) newWidth;
    double nHeightFactor = (double) img.getHeight() / (double) newHeight;

    double fx, fy, nx, ny;
    int cx, cy, fr_x, fr_y;
    int color1;
    int color2;
    int color3;
    int color4;
    byte nRed, nGreen, nBlue;

    byte bp1, bp2;

    for (int x = 0; x < bmap.getWidth(); ++x) {
        for (int y = 0; y < bmap.getHeight(); ++y) {

            fr_x = (int) Math.floor(x * nWidthFactor);
            fr_y = (int) Math.floor(y * nHeightFactor);
            cx = fr_x + 1;
            if (cx >= img.getWidth())
                cx = fr_x;
            cy = fr_y + 1;
            if (cy >= img.getHeight())
                cy = fr_y;
            fx = x * nWidthFactor - fr_x;
            fy = y * nHeightFactor - fr_y;
            nx = 1.0 - fx;
            ny = 1.0 - fy;

            color1 = img.getPixel(fr_x, fr_y);
            color2 = img.getPixel(cx, fr_y);
            color3 = img.getPixel(fr_x, cy);
            color4 = img.getPixel(cx, cy);

            // Blue
            bp1 = (byte) (nx * Color.blue(color1) + fx * Color.blue(color2));
            bp2 = (byte) (nx * Color.blue(color3) + fx * Color.blue(color4));
            nBlue = (byte) (ny * (double) (bp1) + fy * (double) (bp2));

            // Green
            bp1 = (byte) (nx * Color.green(color1) + fx * Color.green(color2));
            bp2 = (byte) (nx * Color.green(color3) + fx * Color.green(color4));
            nGreen = (byte) (ny * (double) (bp1) + fy * (double) (bp2));

            // Red
            bp1 = (byte) (nx * Color.red(color1) + fx * Color.red(color2));
            bp2 = (byte) (nx * Color.red(color3) + fx * Color.red(color4));
            nRed = (byte) (ny * (double) (bp1) + fy * (double) (bp2));

            bmap.setPixel(x, y, Color.argb(255, nRed, nGreen, nBlue));
        }
    }

    bmap = setGrayscale(bmap);
    bmap = removeNoise(bmap);

    return bmap;
}

// SetGrayscale
private Bitmap setGrayscale(Bitmap img) {
    Bitmap bmap = img.copy(img.getConfig(), true);
    int c;
    for (int i = 0; i < bmap.getWidth(); i++) {
        for (int j = 0; j < bmap.getHeight(); j++) {
            c = bmap.getPixel(i, j);
            byte gray = (byte) (.299 * Color.red(c) + .587 * Color.green(c)
                    + .114 * Color.blue(c));

            bmap.setPixel(i, j, Color.argb(255, gray, gray, gray));
        }
    }
    return bmap;
}

// RemoveNoise
private Bitmap removeNoise(Bitmap bmap) {
    for (int x = 0; x < bmap.getWidth(); x++) {
        for (int y = 0; y < bmap.getHeight(); y++) {
            int pixel = bmap.getPixel(x, y);
            if (Color.red(pixel) < 162 && Color.green(pixel) < 162 && Color.blue(pixel) < 162) {
                bmap.setPixel(x, y, Color.BLACK);
            }
        }
    }
    for (int x = 0; x < bmap.getWidth(); x++) {
        for (int y = 0; y < bmap.getHeight(); y++) {
            int pixel = bmap.getPixel(x, y);
            if (Color.red(pixel) > 162 && Color.green(pixel) > 162 && Color.blue(pixel) > 162) {
                bmap.setPixel(x, y, Color.WHITE);
            }
        }
    }
    return bmap;
}

Адаптивный порог важен, если освещение на изображении неравномерно. Моя предварительная обработка с использованием GraphicsMagic упоминается в этом посте: https://groups.google.com/forum/

В GraphicsMagic также есть функция -lat для линейного адаптивного порога, которую я скоро опробую.

Другой метод порогового использования с использованием OpenCV описан здесь: http://docs.opencv.org/trunk/doc/py_tutorials/py_imgproc/py_thresholding/py_thresholding.html

Я сделал это, чтобы получить хорошие результаты из изображения, которое имеет не очень маленький текст.

  1. Примените размытие к исходному изображению.
  2. Применить адаптивный порог.
  3. Применить эффект заточки.

И если все еще не получаются хорошие результаты, масштабируйте изображение до 150% или 200%.

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

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

2) Неправильная ориентация изображения: из-за неправильной ориентации механизм распознавания не может правильно сегментировать строки и слова на изображении, что дает наихудшую точность.

3) Наличие строк: при выполнении сегментации слова или строки механизм OCR иногда также пытается объединить слова и строки вместе, обрабатывая, таким образом, неверный контент и, следовательно, давая неправильные результаты. Есть и другие проблемы, но они являются основными.

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

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

Я написал подробную статью по обработке изображений в Python. Пожалуйста, перейдите по ссылке ниже для более подробного объяснения. Также добавлен исходный код Python для реализации этого процесса.

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

https://medium.com/cashify-engineering/improve-accuracy-of-ocr-using-image-preprocessing-8df29ec3a033

До сих пор я много играл с tesseract 3.x, 4.x и 5.0.0. tesseract 4.x и 5.x дают одинаковую точность.

Иногда я получаю лучшие результаты с устаревшим движком (используя --oem 0), и иногда я получаю лучшие результаты с движком LTSM --oem 1. Вообще говоря, я получаю наилучшие результаты на увеличенных изображениях с движком LTSM. Последний не уступает моему предыдущему движку (ABBYY CLI OCR 11 для Linux).

Конечно, обученные данные необходимо загрузить с github, так как большинство дистрибутивов Linux предоставляют только быстрые версии. Обученные данные, которые будут работать как для устаревших, так и для LTSM-движков, можно загрузить по адресу https://github.com/tesseract-ocr/tessdata с помощью следующей команды. Не забудьте также загрузить обученные данные OSD.

      curl -L https://github.com/tesseract-ocr/tessdata/blob/main/eng.traineddata?raw=true -o /usr/share/tesseract/tessdata/eng.traineddata
curl -L https://github.com/tesseract-ocr/tessdata/blob/main/eng.traineddata?raw=true -o /usr/share/tesseract/tessdata/osd.traineddata

В итоге я использовал ImageMagick в качестве препроцессора изображений, поскольку он удобен и может легко запускаться по сценарию. Вы можете установить его с помощью yum install ImageMagickили же apt install imagemagickв зависимости от вкуса вашего дистрибутива.

Итак, вот мой препроцессор oneliner, который подходит для большинства вещей, которые я передаю в OCR:

      convert my_document.jpg -units PixelsPerInch -respect-parenthesis \( -compress LZW -resample 300 -bordercolor black -border 1 -trim +repage -fill white -draw "color 0,0 floodfill" -alpha off -shave 1x1 \) \( -bordercolor black -border 2 -fill white -draw "color 0,0 floodfill" -alpha off -shave 0x1 -deskew 40 +repage \) -antialias -sharpen 0x3 preprocessed_my_document.tiff

В основном мы:

  • используйте формат TIFF, так как tesseract любит его больше, чем JPG (связанный с декомпрессором, кто знает)
  • использовать сжатие LZW TIFF без потерь
  • Увеличьте разрешение изображения до 300 dpi.
  • Используйте черную магию, чтобы удалить нежелательные цвета
  • Попробуйте повернуть страницу, если поворот можно обнаружить
  • Сглаживание изображения
  • Повышение резкости текста

Последнее изображение можно передать в тессеракт с помощью:

      tesseract -l eng preprocessed_my_document.tiff - --oem 1 -psm 1

Кстати, несколько лет назад я написал «сервер OCR для бедняков», который проверяет измененные файлы в заданном каталоге и запускает операции OCR для всех еще не обработанных OCR файлов. pmocr совместим с tesseract 3.x-5.x и abbyyocr11. См . проект pmocr на github .

Вы можете сделать шумоподавление, а затем применить пороговое значение, но вы можете поиграть с конфигурацией OCR, изменив значения --psm и --oem

попробуйте: --psm 5 --oem 2

вы также можете посмотреть следующую ссылку для получения дополнительной информации здесь

Другие вопросы по тегам