Как обнаружить и распознать текст с картинки

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

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

Определить участки изображения, в которых присутствует текст, используя MSER

В функции MSER детектор хорошо работает для поиска регионов с содержанием текстовых символов. Она неплохо выполняет свою работу, поскольку последовательные цвет и высокая контрастность текста приводит к прочным профилям интенсивности.

Используя функцию detectMSERFeatures найдем образы и сюжет всех регионов. Обратите внимание, что функция также выделяет много нетекстовых областей:

colorImage = imread('handicapSign.jpg');
I = rgb2gray(colorImage);

% Detect MSER regions.
[mserRegions, mserConnComp] = detectMSERFeatures(I, ...
  'RegionAreaRange',[200 8000],'ThresholdDelta',4);

figure
imshow(I)
hold on
plot(mserRegions, 'showPixelList', true,'showEllipses',false)
title('MSER regions')
hold off
Распознать текст с картинки

Удалить нетекстовые области

Функция MSER направлена на распознавание текстовых областей, но во время своей работы он также обнаруживает множество других нетекстовых регионов. Мы можем обойти все выделенные области для того, чтобы удалить ненужные, нетекстовые регионы. Для фильтрации нетекстовых областей можно использовать геометрические свойства текста. Также мы можем воспользоваться подходом машинного обучения для подготовки текстовых и нетекстовых классификаторов. Как правило, сочетание этих двух подходов дает лучшие результаты. В этом примере мы будем использовать простой подход фильтрации нетекстовых регионов на основе геометрических свойств.

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

  • Соотношение сторон;
  • Эксцентриситет;
  • Число Эйлера;
  • Степени;
  • Солидность;

Воспользуемся функцией regionprops для измерения этих свойств, а затем начнем удалять регионы, которые нам не подходят:

% Use regionprops to measure MSER properties
mserStats = regionprops(mserConnComp, 'BoundingBox', 'Eccentricity', ...
  'Solidity', 'Extent', 'Euler', 'Image');

% Compute the aspect ratio using bounding box data.
bbox = vertcat(mserStats.BoundingBox);
w = bbox(:,3);
h = bbox(:,4);
aspectRatio = w./h;

% Threshold the data to determine which regions to remove. These thresholds
% may need to be tuned for other images.
filterIdx = aspectRatio' > 3;
filterIdx = filterIdx | [mserStats.Eccentricity] > .995 ;
filterIdx = filterIdx | [mserStats.Solidity] < .3;
filterIdx = filterIdx | [mserStats.Extent] < 0.2 | [mserStats.Extent] > 0.9;
filterIdx = filterIdx | [mserStats.EulerNumber] < -4;

% Remove regions
mserStats(filterIdx) = [];
mserRegions(filterIdx) = [];

% Show remaining regions
figure
imshow(I)
hold on
plot(mserRegions, 'showPixelList', true,'showEllipses',false)
title('After Removing Non-Text Regions Based On Geometric Properties')
hold off
Распознать текст с картинки

Удаление нетекстовых регионов на основе изменения ширины контуров

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

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

% Get a binary image of the a region, and pad it to avoid boundary effects
% during the stroke width computation.
regionImage = mserStats(6).Image;
regionImage = padarray(regionImage, [1 1]);

% Compute the stroke width image.
distanceImage = bwdist(~regionImage);
skeletonImage = bwmorph(regionImage, 'thin', inf);

strokeWidthImage = distanceImage;
strokeWidthImage(~skeletonImage) = 0;

% Show the region image alongside the stroke width image.
figure
subplot(1,2,1)
imagesc(regionImage)
title('Region Image')

subplot(1,2,2)
imagesc(strokeWidthImage)
title('Stroke Width Image')
Распознать текст с картинки

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

Для того, чтобы использовать метод изменения ширины штриха, необходимо ввести пороговое значение, изменения по всей области следующим образом:

% Compute the stroke width variation metric
strokeWidthValues = distanceImage(skeletonImage);
strokeWidthMetric = std(strokeWidthValues)/mean(strokeWidthValues);

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

% Threshold the stroke width variation metric
strokeWidthThreshold = 0.4;
strokeWidthFilterIdx = strokeWidthMetric > strokeWidthThreshold;

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

% Process the remaining regions
for j = 1:numel(mserStats)

  regionImage = mserStats(j).Image;
  regionImage = padarray(regionImage, [1 1], 0);

  distanceImage = bwdist(~regionImage);
  skeletonImage = bwmorph(regionImage, 'thin', inf);

  strokeWidthValues = distanceImage(skeletonImage);

  strokeWidthMetric = std(strokeWidthValues)/mean(strokeWidthValues);

  strokeWidthFilterIdx(j) = strokeWidthMetric > strokeWidthThreshold;

end

% Remove regions based on the stroke width variation
mserRegions(strokeWidthFilterIdx) = [];
mserStats(strokeWidthFilterIdx) = [];

% Show remaining regions
figure
imshow(I)
hold on
plot(mserRegions, 'showPixelList', true,'showEllipses',false)
title('After Removing Non-Text Regions Based On Stroke Width Variation')
hold off
Распознать текст с картинки

Объединение полученных областей для окончательного результата обнаружения текста

На данный момент, все результаты обнаружения состоят из отдельных текстовых символов. Чтобы использовать эти результаты для задач распознавания, отдельные символы текста должны быть объединены в слова или строки. Это позволяет распознавать слова в изображении, которые несут более значимую информацию, чем просто отдельные буквы.

Для того чтобы, объединить отдельные регионы в слова или строки текста, необходимо сначала найти текст из соседних регионов, а затем сформировать рамку вокруг этих регионов. Чтобы найти соседние регионы нужно расширить рамки, вычисленные ранее regionprops.

% Get bounding boxes for all the regions
bboxes = vertcat(mserStats.BoundingBox);

% Convert from the [x y width height] bounding box format to the [xmin ymin
% xmax ymax] format for convenience.
xmin = bboxes(:,1);
ymin = bboxes(:,2);
xmax = xmin + bboxes(:,3) - 1;
ymax = ymin + bboxes(:,4) - 1;

% Expand the bounding boxes by a small amount.
expansionAmount = 0.02;
xmin = (1-expansionAmount) * xmin;
ymin = (1-expansionAmount) * ymin;
xmax = (1+expansionAmount) * xmax;
ymax = (1+expansionAmount) * ymax;

% Clip the bounding boxes to be within the image bounds
xmin = max(xmin, 1);
ymin = max(ymin, 1);
xmax = min(xmax, size(I,2));
ymax = min(ymax, size(I,1));

% Show the expanded bounding boxes
expandedBBoxes = [xmin ymin xmax-xmin+1 ymax-ymin+1];
IExpandedBBoxes = insertShape(colorImage,'Rectangle',expandedBBoxes,'LineWidth',3);

figure
imshow(IExpandedBBoxes)
title('Expanded Bounding Boxes Text')
Распознать текст с картинки

Теперь, перекрывающиеся рамки могут быть объединены вместе, чтобы сформировать один ограничивающий прямоугольник вокруг отдельных слов или строк текста. Для этого вычисляют коэффициент перекрытия между всеми парами ограничивающего прямоугольника. Это определяет расстояние между всеми парами текстовых регионов, так чтобы в нем можно найти группы из соседних регионов имеющие ненулевые коэффициенты перекрытия. После попарного перекрытия используя graph вычисляются коэффициенты, чтобы найти все текстовые регионов «связанные» с ненулевыми коэффициентами перекрытия.

Мы будем использовать функцию bboxOverlapRatio, для вычисления парных коэффициентов перекрытия для всех расширенных рамок, а затем воспользуемся graph для поиска всех подключенных регионов.

% Compute the overlap ratio
overlapRatio = bboxOverlapRatio(expandedBBoxes, expandedBBoxes);

% Set the overlap ratio between a bounding box and itself to zero to
% simplify the graph representation.
n = size(overlapRatio,1);
overlapRatio(1:n+1:n^2) = 0;

% Create the graph
g = graph(overlapRatio);

% Find the connected text regions within the graph
componentIndices = conncomp(g);

Выходные данные conncomp являются индекса регионов содержащих текст ограниченные рамками. Используя эти показатели, мы можем объединить несколько соседних рамок, в единую рамку путем вычисления минимального и максимального из индивидуальных ограничительных блоков, которые составляют каждую компоненту связности.

% Merge the boxes based on the minimum and maximum dimensions.
xmin = accumarray(componentIndices', xmin, [], @min);
ymin = accumarray(componentIndices', ymin, [], @min);
xmax = accumarray(componentIndices', xmax, [], @max);
ymax = accumarray(componentIndices', ymax, [], @max);

% Compose the merged bounding boxes using the [x y width height] format.
textBBoxes = [xmin ymin xmax-xmin+1 ymax-ymin+1];

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

% Remove bounding boxes that only contain one text region
numRegionsInGroup = histcounts(componentIndices);
textBBoxes(numRegionsInGroup == 1, :) = [];

% Show the final text detection result.
ITextRegion = insertShape(colorImage, 'Rectangle', textBBoxes,'LineWidth',3);

figure
imshow(ITextRegion)
title('Detected Text')
Распознать текст с картинки

Распознать текст с помощью OCR

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

ocrtxt = ocr(I, textBBoxes);
[ocrtxt.Text]

ans =

'HANDICIXPPED PARKING SPECIAL PLATE REQUIRED UNAUTHORIZED VEHICLES MAY BE TOWED AT OWNERS EXPENSE

Таким образом, мы смогли распознать текст из картинки.

Добавить комментарий


Защитный код
Обновить