обработка изображений для повышения точности распознавания текста - PullRequest
119 голосов
/ 28 февраля 2012

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

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

Любые общие советы для новичкапри обработке изображения?

Ответы [ 12 ]

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

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

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

66 голосов
/ 24 июля 2012

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

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

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

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

28 голосов
/ 10 декабря 2014

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

16 голосов
/ 28 мая 2013

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

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

Попробуйте разные режимы интерполяции. Пост https://stackoverflow.com/a/4756906/146003 мне очень помог.

13 голосов
/ 21 марта 2014

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

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

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

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

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

12 голосов
/ 08 июня 2018

Как правило, я обычно применяю следующие методы предварительной обработки изображений с использованием библиотеки 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)
    

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

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

12 голосов
/ 20 июля 2016

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;
}
6 голосов
/ 17 января 2015

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

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

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

5 голосов
/ 15 октября 2017

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

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

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

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

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

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

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

2 голосов
/ 23 октября 2017

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

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

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

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

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

...