Подтвердить что ты не робот

Удалите фоновый шум с изображения, чтобы сделать текст более понятным для OCR

Я написал приложение, которое сегментирует изображение на основе текстовых областей внутри него и извлекает эти области по своему усмотрению. То, что я пытаюсь сделать, - это очистить изображение, чтобы OCR (Tesseract) дал точный результат. В качестве примера у меня есть следующее изображение:

введите описание изображения здесь

Выполнение этого через tesseract дает очень неточный результат. Однако очистка изображения (с помощью фотошопа), чтобы получить изображение следующим образом:

введите описание изображения здесь

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

 public Mat cleanImage (Mat srcImage) {
    Core.normalize(srcImage, srcImage, 0, 255, Core.NORM_MINMAX);
    Imgproc.threshold(srcImage, srcImage, 0, 255, Imgproc.THRESH_OTSU);
    Imgproc.erode(srcImage, srcImage, new Mat());
    Imgproc.dilate(srcImage, srcImage, new Mat(), new Point(0, 0), 9);
    return srcImage;
}

Что еще можно сделать, чтобы очистить первое изображение, чтобы оно было похоже на второе изображение?

Изменить: это исходное изображение перед запуском функции cleanImage.

введите описание изображения здесь

4b9b3361

Ответ 1

Мой ответ основан на следующих предположениях. Возможно, что ни один из них не имеет места в вашем случае.

  • Возможно, вы наложили пороговое значение для предельных высот ящиков в сегментированной области. Затем вы сможете отфильтровать другие компоненты.
  • Вы знаете среднюю ширину штрихов цифр. Используйте эту информацию, чтобы свести к минимуму вероятность подключения цифр к другим регионам. Для этого вы можете использовать преобразование расстояния и морфологические операции.

Это моя процедура для извлечения цифр:

  • Применить порог Otsu к изображению otsu
  • Возьмите преобразование расстояния dist
  • Порог преобразованного расстояния с использованием ограничения ширины штриха (= 8) sw2

  • Применить морфологическую операцию для отключения ws2op

  • Отфильтруйте высоты ограничивающих окон и сделайте предположение, где цифры

stroke-width = 8 bb ширина хода = 10 bb2

ИЗМЕНИТЬ

  • Подготовьте маску, используя выпуклость найденных контуров цифр mask

  • Скопируйте область цифр в чистое изображение с помощью маски

stroke-width = 8 cl1

stroke-width = 10 cl2

Знания Мои Тессеракт немного ржавые. Насколько я помню, вы можете получить уровень достоверности для персонажей. Вы можете фильтровать шум, используя эту информацию, если вы все еще обнаружите шумные области как ограничивающие символы.

Код С++

Mat im = imread("aRh8C.png", 0);
// apply Otsu threshold
Mat bw;
threshold(im, bw, 0, 255, CV_THRESH_BINARY_INV | CV_THRESH_OTSU);
// take the distance transform
Mat dist;
distanceTransform(bw, dist, CV_DIST_L2, CV_DIST_MASK_PRECISE);
Mat dibw;
// threshold the distance transformed image
double SWTHRESH = 8;    // stroke width threshold
threshold(dist, dibw, SWTHRESH/2, 255, CV_THRESH_BINARY);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
// perform opening, in case digits are still connected
Mat morph;
morphologyEx(dibw, morph, CV_MOP_OPEN, kernel);
dibw.convertTo(dibw, CV_8U);
// find contours and filter
Mat cont;
morph.convertTo(cont, CV_8U);

Mat binary;
cvtColor(dibw, binary, CV_GRAY2BGR);

const double HTHRESH = im.rows * .5;    // height threshold
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
vector<Point> digits; // points corresponding to digit contours

findContours(cont, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
for(int idx = 0; idx >= 0; idx = hierarchy[idx][0])
{
    Rect rect = boundingRect(contours[idx]);
    if (rect.height > HTHRESH)
    {
        // append the points of this contour to digit points
        digits.insert(digits.end(), contours[idx].begin(), contours[idx].end());

        rectangle(binary, 
            Point(rect.x, rect.y), Point(rect.x + rect.width - 1, rect.y + rect.height - 1),
            Scalar(0, 0, 255), 1);
    }
}

// take the convexhull of the digit contours
vector<Point> digitsHull;
convexHull(digits, digitsHull);
// prepare a mask
vector<vector<Point>> digitsRegion;
digitsRegion.push_back(digitsHull);
Mat digitsMask = Mat::zeros(im.rows, im.cols, CV_8U);
drawContours(digitsMask, digitsRegion, 0, Scalar(255, 255, 255), -1);
// expand the mask to include any information we lost in earlier morphological opening
morphologyEx(digitsMask, digitsMask, CV_MOP_DILATE, kernel);
// copy the region to get a cleaned image
Mat cleaned = Mat::zeros(im.rows, im.cols, CV_8U);
dibw.copyTo(cleaned, digitsMask);

ИЗМЕНИТЬ

Код Java

Mat im = Highgui.imread("aRh8C.png", 0);
// apply Otsu threshold
Mat bw = new Mat(im.size(), CvType.CV_8U);
Imgproc.threshold(im, bw, 0, 255, Imgproc.THRESH_BINARY_INV | Imgproc.THRESH_OTSU);
// take the distance transform
Mat dist = new Mat(im.size(), CvType.CV_32F);
Imgproc.distanceTransform(bw, dist, Imgproc.CV_DIST_L2, Imgproc.CV_DIST_MASK_PRECISE);
// threshold the distance transform
Mat dibw32f = new Mat(im.size(), CvType.CV_32F);
final double SWTHRESH = 8.0;    // stroke width threshold
Imgproc.threshold(dist, dibw32f, SWTHRESH/2.0, 255, Imgproc.THRESH_BINARY);
Mat dibw8u = new Mat(im.size(), CvType.CV_8U);
dibw32f.convertTo(dibw8u, CvType.CV_8U);

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3));
// open to remove connections to stray elements
Mat cont = new Mat(im.size(), CvType.CV_8U);
Imgproc.morphologyEx(dibw8u, cont, Imgproc.MORPH_OPEN, kernel);
// find contours and filter based on bounding-box height
final double HTHRESH = im.rows() * 0.5; // bounding-box height threshold
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
List<Point> digits = new ArrayList<Point>();    // contours of the possible digits
Imgproc.findContours(cont, contours, new Mat(), Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE);
for (int i = 0; i < contours.size(); i++)
{
    if (Imgproc.boundingRect(contours.get(i)).height > HTHRESH)
    {
        // this contour passed the bounding-box height threshold. add it to digits
        digits.addAll(contours.get(i).toList());
    }   
}
// find the convexhull of the digit contours
MatOfInt digitsHullIdx = new MatOfInt();
MatOfPoint hullPoints = new MatOfPoint();
hullPoints.fromList(digits);
Imgproc.convexHull(hullPoints, digitsHullIdx);
// convert hull index to hull points
List<Point> digitsHullPointsList = new ArrayList<Point>();
List<Point> points = hullPoints.toList();
for (Integer i: digitsHullIdx.toList())
{
    digitsHullPointsList.add(points.get(i));
}
MatOfPoint digitsHullPoints = new MatOfPoint();
digitsHullPoints.fromList(digitsHullPointsList);
// create the mask for digits
List<MatOfPoint> digitRegions = new ArrayList<MatOfPoint>();
digitRegions.add(digitsHullPoints);
Mat digitsMask = Mat.zeros(im.size(), CvType.CV_8U);
Imgproc.drawContours(digitsMask, digitRegions, 0, new Scalar(255, 255, 255), -1);
// dilate the mask to capture any info we lost in earlier opening
Imgproc.morphologyEx(digitsMask, digitsMask, Imgproc.MORPH_DILATE, kernel);
// cleaned image ready for OCR
Mat cleaned = Mat.zeros(im.size(), CvType.CV_8U);
dibw8u.copyTo(cleaned, digitsMask);
// feed cleaned to Tesseract

Ответ 2

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

Мои идеи для этого:

1- Извлечение контуров из изображения и поиск контуров на изображении (отметьте this) и this

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

3- После фильтрации контур вы можете проверить, где находятся буквы и цифры на этом изображении, поэтому вам может понадобиться использовать некоторые методы обнаружения текста, такие как здесь

4- Все, что вам нужно сейчас, если удалить нетекстовую область и контуры, которые не хороши для изображения

5 Теперь вы можете создать свой метод биниризации, или вы можете использовать tesseract для выполнения биниризации на изображении, а затем вызвать OCR на изображении.

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

Другие идеи:

  • Вы можете использовать различные способы сделать это, лучшая идея - найти способ обнаружения местоположения цифр и символов с использованием различных методов, таких как сопоставление шаблонов или функции, основанной на HOG.

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

  • После обнаружения всех контуров на изображении вы также можете использовать Hough transformation для обнаружения любой линии и определенной кривой, такой как one, и таким образом вы можете обнаружить символы, которые выровнены, чтобы вы могли сегментировать изображение и делать OCR после этого.

Намного проще:

1- Сделайте биниризацию введите описание изображения здесь

2- Некоторая морфологическая операция для разделения контуров:

введите описание изображения здесь

3- Обратный цвет изображения (это может быть до этапа 2)

введите описание изображения здесь

4- Найти все контуры изображения

введите описание изображения здесь

5- Удалите все контуры, ширина которых больше высоты, удалите очень маленькие контуры, очень большие и контуры не прямоугольника

введите описание изображения здесь

Примечание: вы можете использовать методы обнаружения текста (или используя HOG или обнаружение краев) вместо шагов 4 и 5

6- Найдите большой прямоугольник, содержащий все остальные контуры изображения

введите описание изображения здесь

7- Вы можете сделать дополнительную предварительную обработку для улучшения ввода для tesseract, после чего вы можете вызвать OCR сейчас. (Я советую вам обрезать изображение и сделать его как вход для OCR [я имею в виду обрезать желтый прямоугольник и не делать все изображение как входным только желтым прямоугольником, и это также улучшит результаты))

Ответ 3

Помогло бы вам это изображение?

введите описание изображения здесь

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

Я тестировал все изображения с помощью tesseract:

  • Исходное изображение: ничего не обнаружено
  • Обработанное изображение # 1: ничего не обнаружено
  • Обработанное изображение # 2: 12-14 (точное совпадение)
  • Мое обработанное изображение: y1'2-14/j

Ответ 4

Просто немного подумайте из коробки:

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

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

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

Затем следуйте остальным шагам, которые вы уже выполняете: уменьшите шум, чтобы удалить тончайшие детали (т.е. фоновый рисунок, который выглядит как водяной знак безопасности или голограмма в значке). Результат должен быть достаточно ясным, чтобы Tesseract работал без проблем.

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

Ответ 5

Размер шрифта не должен быть таким большим или малым, примерно он должен находиться в диапазоне 10-12 pt (т.е. высота символов примерно выше 20 и менее 80). вы можете опробовать изображение и попробовать с помощью tesseract. И несколько шрифтов не обучены tesseract, проблема может возникнуть, если это не в том, что обученные шрифты.