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

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

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

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

Ответ 1

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

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

Если вы не являетесь поклонником командной строки, возможно, вы можете попробовать использовать openource scantailor.sourceforge.net или коммерческий книжный реставратор.

Ответ 2

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

Я начал с раскрашенного RGB 445x747 пикселя jpg.  Я тут же попробовал tesseract на этом, и программа практически ничего не конвертировала. Затем я вошел в GIMP и сделал следующее. Изображение > Режим > полутоновой image > масштабное изображение > 1191x2000 пикселей фильтры > усиление > нерезкая маска со значениями радиуса = 6.8, величина = 2.69, порог = 0 Затем я сохранил как новый jpg со 100% качеством.

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

Gimp - ваш друг.

Ответ 3

Три точки для улучшения читаемости изображения: 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;
        }

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

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

Ответ 4

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

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

Попробуйте различные способы интерполяции. Сообщение fooobar.com/questions/59776/... мне очень помогло.

Ответ 5

Что было Чрезвычайно полезным для меня на этом пути, это исходные коды для проекта Capture2Text. http://sourceforge.net/projects/capture2text/files/Capture2Text/.

BTW: Престижность к этому автору для совместного использования такого кропотливого алгоритма.

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

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

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

Ответ 6

Версия Java для кода Sathyaraj выше:

// 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;
}

Ответ 7

Адаптивное пороговое значение важно, если освещение неравномерно по всему изображению. Моя предварительная обработка с использованием GraphicsMagic упоминается в этом сообщении: https://groups.google.com/forum/#!topic/tesseract-ocr/jONGSChLRv4

GraphicsMagic также имеет функцию -lat для линейного времени Adaptive Threshold, которое я попробую в ближайшее время.

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

Ответ 8

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

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

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

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

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

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

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

Ответ 9

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

  1. Изменение изображения (рекомендуется, если вы работаете с изображениями с DPI менее 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 и прояснить некоторые препятствия, которые я испытал, когда ситуация была менее ясной, чем хотелось бы в документации.

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

Ответ 10

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

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

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

Ответ 11

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

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

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

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

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

Ответ 12

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

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

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

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