Локализация и чтение нескольких штрихкодов в изображении

В этом примере показано, как использовать readBarcode функция из Computer Vision Toolbox™ для обнаружения и декодирования 1-D и 2-D штрихкодов в изображении. Штрих-коды широко используются для кодирования данных в визуальном, машиночитаемом формате. Они применяются во многих приложениях, таких как идентификация элемента, отслеживание складских запасов и отслеживание податливости нормативам. Для 1-D штрихкодов readBarcode функция возвращает местоположение конечных точек штрих-кода. Для 2-D штрихкодов функция возвращает местоположения шаблонов поиска. Этот пример использует два подхода для локализации нескольких штрихкодов в изображении. Одним из подходов является основанная на кластеризации, которая более устойчива к различным условиям визуализации и требует Statistics and Machine Learning Toolbox™. Второй подход использует рабочий процесс на основе сегментации и может потребовать настройки параметра на основе условий визуализации.

Обнаружение штрих-кода с использованием readBarcode Функция

Чтение QR- кода с изображения.

I = imread("barcodeQR.jpg");

% Search the image for a QR Code.
[msg, ~, loc] = readBarcode(I);

% Annotate the image with the decoded message.
xyText =  loc(2,:);
Imsg = insertText(I, xyText, msg, "BoxOpacity", 1, "FontSize", 25);

% Insert filled circles at the finder pattern locations.
Imsg = insertShape(Imsg, "FilledCircle", [loc, ...
    repmat(10, length(loc), 1)], "Color", "red", "Opacity", 1);

% Display image.
imshow(Imsg)

Figure contains an axes. The axes contains an object of type image.

Считайте 1-D штрих-код с изображения.

I = imread("barcode1D.jpg");

% Read the 1-D barcode and determine the format..
[msg, format, locs] = readBarcode(I);

% Display the detected message and format.
disp("Detected format and message: " + format + ", " + msg)
Detected format and message: EAN-13, 1234567890128
% Insert a line to show the scan row of the barcode.
xyBegin = locs(1,:); imSize = size(I);
I = insertShape(I,"Line",[1 xyBegin(2) imSize(2) xyBegin(2)], ...
    "LineWidth", 7);

% Insert markers at the end locations of the barcode.
I = insertShape(I, "FilledCircle", [locs, ...
    repmat(10, length(locs), 1)], "Color", "red", "Opacity", 1);

% Display image.
imshow(I)

Figure contains an axes. The axes contains an object of type image.

Улучшение обнаружения штрих-кода

Для успешного обнаружения штрих-код должен быть четко виден. Штрих-код также должен быть максимально выровнен по горизонтали или вертикали. The readBarcode функция по своей сути более устойчива к вращениям для 2-D или матричных кодов, чем для 1-D или линейных штрихкодов. Для примера штрих-код не может быть обнаружен в этом изображении.

I = imread("rotated1DBarcode.jpg");

% Display the image.
imshow(I)

Figure contains an axes. The axes contains an object of type image.

% Pass the image to the readBarcode function.
readBarcode(I)
ans = 
""

Поверните изображение с помощью imrotate так, чтобы штрихкод был ориентировочно горизонтальным. Использование readBarcode на повернутом изображении.

% Rotate the image by 30 degrees clockwise.
Irot = imrotate(I, -30);

% Display the rotated image.
imshow(Irot)

Figure contains an axes. The axes contains an object of type image.

% Pass the rotated image to the readBarcode function.
readBarcode(Irot)
ans = 
"012345678905"

Обнаружение нескольких штрихкодов

The readBarcode функция обнаруживает только один штрих-код в каждом изображении. В порядок обнаружения нескольких штрихкодов необходимо задать видимую область (ROI). Чтобы задать информация только для чтения, можно использовать drawrectangle функция для интерактивного определения ROIs. Можно также использовать методы анализа изображений, чтобы обнаружить информация только для чтения нескольких штрихкодов на изображении.

Интерактивное определение ROI

I = imread("multiple1DBarcodes.jpg");

Используйте drawrectangle функция для рисования и получения параметров прямоугольника.

roi1 = drawrectangle;

pos = roi1.Position;

% ROIs obtained using drawrectangle
roi = [180 100 330 180
    180 320 330 180
    180 550 330 180];

imSize = size(I);
for i = 1:size(roi,1)
    [msg, format, locs] = readBarcode(I, roi(i,:));
    disp("Decoded format and message: " + format + ", " + msg)
    
    % Insert a line to indicate the scan row of the barcode.
    xyBegin = locs(1,:);
    I = insertShape(I,"Line",[1 xyBegin(2) imSize(2) xyBegin(2)], ...
        "LineWidth", 5);
    
    % Annotate image with decoded message.
    I = insertText(I, xyBegin, msg, "BoxOpacity", 1, "FontSize", 20);
end
Decoded format and message: UPC-A, 012345678905
Decoded format and message: EAN-13, 4567891324562
Decoded format and message: CODE-39, ABC-123
imshow(I)

Figure contains an axes. The axes contains an object of type image.

Анализ изображений для определения ROI

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

I = imread("multiple1DBarcodesRotated.jpg");
Igray = im2gray(I);

% Display the image.
imshow(I)

Figure contains an axes. The axes contains an object of type image.

% Pass the unprocessed image to the readBarcode function.
readBarcode(Igray, '1D')
ans = 
""

Обнаружение необработанного изображения не привело к обнаружению.

Шаг 1: Обнаружение областей кандидата для штрихкодов с помощью MSER

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

% Detect MSER features.
[~, cc] = detectMSERFeatures(Igray);

% Compute region properties MajorAxisLength and MinorAxisLength.
regionStatistics = regionprops(cc, 'MajorAxisLength', 'MinorAxisLength');

% Filter out components that have a low aspect ratio as unsuitable
% candidates for the bars in the barcode.
minAspectRatio = 10;
candidateRegions = find(([regionStatistics.MajorAxisLength]./[regionStatistics.MinorAxisLength]) > minAspectRatio);

% Binary image to store the filtered components.
BW = false(size(Igray));

% Update the binary image.
for i = 1:length(candidateRegions)
    BW(cc.PixelIdxList{candidateRegions(i)}) = true;
end

% Display the binary image with the filtered components.
imshow(BW)
title("Candidate regions for the barcodes")

Figure contains an axes. The axes with title Candidate regions for the barcodes contains an object of type image.

Шаг 2: Извлечение сегментов линии штрихкода с помощью преобразования Хафа

Обнаружение видных ребер на изображении с помощью edge функция. Затем используйте преобразование Хафа, чтобы найти интересующие вас линии. Эти линии представляют возможные кандидаты для вертикальных полос в штрих-коде.

% Perform hough transform.
BW = edge(BW,'canny');
[H,T,R] = hough(BW);

% Display the result of the edge detection operation.
imshow(BW)

Figure contains an axes. The axes contains an object of type image.

% Determine the size of the suppression neighborhood.
reductionRatio = 500;
nhSize = floor(size(H)/reductionRatio);
idx = mod(nhSize,2) < 1;
nhSize(idx) = nhSize(idx) + 1;

% Identify the peaks in the Hough transform.
P  = houghpeaks(H,length(candidateRegions),'NHoodSize',nhSize);

% Detect the lines based on the detected peaks.
lines = houghlines(BW,T,R,P);

% Display the lines detected using the houghlines function.
Ihoughlines = ones(size(BW));

% Start and end points of the detected lines.
startPts = reshape([lines(:).point1], 2, length(lines))';
endPts = reshape([lines(:).point2], 2, length(lines))';

Ihoughlines = insertShape(Ihoughlines, 'Line', [startPts, endPts], ...
    'LineWidth', 2, 'Color', 'green');

% Display the original image overlayed with the detected lines.
Ibarlines = imoverlay(I, ~Ihoughlines(:,:,1));
imshow(Ibarlines)

Figure contains an axes. The axes contains an object of type image.

Шаг 3: Локализуйте штрихкоды в изображении

После извлечения сегментов линии представлено два метода локализации отдельных штрихкодов на изображении:

  • Метод 1: Метод, основанный на кластеризации, который использует функциональности из Statistics and Machine Learning Toolbox™ для идентификации отдельных штрихкодов. Этот метод является более устойчивым к выбросам, которые были обнаружены с использованием методов анализа изображений выше. Он также может быть расширен до широкой области значений условий визуализации, не требуя настройки параметров.

  • Метод 2: Рабочий процесс на основе сегментации для разделения отдельных штрихкодов. Этот метод использует другие методы анализа изображений, чтобы локализовать и изменить вращение, извлеченные штрихкоды. Хотя это работает довольно хорошо, это может потребовать некоторой настройки параметра, чтобы предотвратить обнаружение выбросов.

Метод 1: Рабочий процесс на основе кластеризации

В этом рабочем процессе есть два шага:

1. Определите бисекторы сегментов линии штрихкода

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

2. Выполните кластеризацию на бисекторах, чтобы идентифицировать отдельные штрихкоды

Поскольку все штриховые штрихи в штрихкоде примерно параллельны друг другу, биссекторы каждой из этих штриховых штрихов должны быть идеально одинаковыми линиями, и поэтому их соответствующие точки должны объединяться вокруг одной точки. На практике эти бисекторы будут варьироваться от сегмента к сегменту, но все еще остаются достаточно похожими, чтобы позволить использовать основанный на плотности алгоритм кластеризации. Результатом выполнения этой операции кластеризации является набор кластеров, каждый из которых указывает на отдельный штрихкод. Этот пример использует dbscan (Statistics and Machine Learning Toolbox) функция, которая не требует предварительного знания количества кластеров. Различные кластеры (штрихкоды) визуализированы в этом примере.

В примере проверяется наличие лицензии Statistics and Machine Learning Toolbox™. Если лицензия найдена, в примере используется метод кластеризации. В противном случае в примере используется метод сегментации.

useClustering = license('test','statistics_toolbox');

if useClustering
    [boundingBox, orientation, Iclusters] = clusteringLocalization(lines, size(I));
    
    % Display the detected clusters.
    imshow(Iclusters)
else
    disp("The clustering based workflow requires a license for the Statistics and Machine Learning Toolbox")
end

Figure contains an axes. The axes contains an object of type image.

Метод 2: Рабочий процесс на основе сегментации

Удалив фоновый шум и изменение, обнаруженные вертикальные планки сгруппированы в отдельные штрихкоды с помощью морфологических операций, таких как imdilate. В примере используется regionprops функция для определения ограничивающего прямоугольника и ориентации для каждого из штрихкодов. Результаты используются, чтобы обрезать отдельные штрихкоды из оригинального изображения и ориентировать их так, чтобы они были примерно горизонтальными.

if ~useClustering
    [boundingBox, orientation, Idilated] = segmentationLocalization(Ihoughlines);
    
    % Display the dilated image.
    imshow(Idilated)
end

Шаг 4: Обрезать штрихкоды и исправить их вращение

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

% Localize and rotate the barcodes in the image.
correctedImages = cell(1, length(orientation));

% Store the cropped and rotation corrected images of the barcodes.
for i = 1:length(orientation)
    
    I = insertShape(I, 'Rectangle', boundingBox(i,:), 'LineWidth',3, 'Color', 'red');
    
    if orientation(i) > 0
        orientation(i) = -(90 - orientation(i));
    else
        orientation(i) = 90 + orientation(i);
    end
    
    % Crop the barcode from the original image and rotate it using the
    % detected orientation.
    correctedImages{i} = imrotate(imcrop(Igray,boundingBox(i,:)), orientation(i));
end

% Display the image with the localized barcodes.
imshow(I)

Figure contains an axes. The axes contains an object of type image.

Шаг 5: Обнаружение штрихкодов в обрезанных и повернутых исправленных изображениях

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

% Pass each of the images to the readBarcode function.
for i = 1:length(correctedImages)
    [msg, format, ~] = readBarcode(correctedImages{i}, '1D');
    disp("Decoded format and message: " + format + ", " + msg)
end
Decoded format and message: UPC-A, 012345678905
Decoded format and message: EAN-13, 4567891324562
Decoded format and message: CODE-39, ABC-123

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

Вспомогательные функции

clusteringLocalization использует рабочий процесс, основанный на кластеризации, для локализации отдельных штрихкодов.

function [boundingBox, orientation, Iclusters] = clusteringLocalization(lines, imSize)

%------------------------------------------------------------------------
% Determine Bisectors of Barcode Line Segments
%------------------------------------------------------------------------

% Table to store the properties of the bisectors of the detected lines.
linesBisector = array2table(zeros(length(lines), 4), 'VariableNames', {'theta', 'rho', 'x', 'y'});

% Use the orientation values of the lines to determine the orientation.
% values of the bisectors
idxNeg = find([lines.theta] < 0);
idxPos = find([lines.theta] >= 0);

negAngles = 90 + [lines(idxNeg).theta];
linesBisector.theta(idxNeg) = negAngles;

posAngles = [lines(idxPos).theta] - 90;
linesBisector.theta(idxPos) = posAngles;

% Determine the midpoints of the detected lines.
midPts = zeros(length(lines),2);

% Determine the 'rho' values of the bisectors.
for i = 1:length(lines)
    midPts(i,:) = (lines(i).point1 + lines(i).point2)/2;
    linesBisector.rho(i) = abs(midPts(i,2) - tand(lines(i).theta) * midPts(i,1))/...
        ((tand(lines(i).theta)^2 + 1) ^ 0.5);
end

% Update the [x,y] locations of the bisectors using their polar
% coordinates.
[linesBisector.x, linesBisector.y] = pol2cart(deg2rad(linesBisector.theta),linesBisector.rho,'ro');

%------------------------------------------------------------------------
% Perform Clustering on the Bisectors to Identity the Individual Barcodes
%------------------------------------------------------------------------

% Store the [x,y] data of the bisectors to be used for clustering.
X = [linesBisector.x,linesBisector.y];

% Get pairwise distance between the points
D = pdist2(X,X);

% Perform density-based spatial clustering to separate the different
% barcodes in the image.
searchRadius = max(imSize/5);
minPoints = 10;
idx = dbscan(D,searchRadius, minPoints);

% Identify the number of clusters (barcodes).
numClusters = unique(idx(idx > 0));

% Store the endpoints of the detected lines.
dataXY = cell(1, length(numClusters));

% Image to show the detected clusters (barcodes).
Iclusters = ones(imSize);

for i = 1:length(numClusters)
    classIdx = find(idx == i);
    
    rgbColor = rand(1,3);
    startPts = reshape([lines(classIdx).point1], 2, length(classIdx))';
    endPts = reshape([lines(classIdx).point2], 2, length(classIdx))';
    
    % Insert lines corresponding to the current cluster (barcode).
    Iclusters = insertShape(Iclusters, 'Line', [startPts, endPts], ...
        'LineWidth', 2, 'Color', rgbColor);
    
    % Update the endpoints of the lines in each cluster (barcode).
    dataXY{i} = [startPts; endPts];
end

%------------------------------------------------------------------------
% Localization parameters for the barcode
%------------------------------------------------------------------------

orientation = zeros(1,length(numClusters));
boundingBox = zeros(length(numClusters), 4);

% Padding the cropped images of barcodes.
padding = 40;

% Determine the ROI and orientation of the individual clusters (barcodes).
for i = 1:length(numClusters)
    
    % Bounding box coordinates with padding.
    x1 = min(dataXY{i}(:,1)) - padding;
    x2 = max(dataXY{i}(:,1)) + padding;
    y1 = min(dataXY{i}(:,2)) - padding;
    y2 = max(dataXY{i}(:,2)) + padding;
    
    boundingBox(i,:) = [x1, y1, x2-x1, y2-y1];
    
    % Orientation of the barcode.
    orientation(i) = mean(linesBisector.theta(idx == i));
    
end

end

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

function [boundingBox, orientation, Idilated] = segmentationLocalization(Ihoughlines)

%------------------------------------------------------------------------
% Use image dilation to separate the barcodes
%------------------------------------------------------------------------

% Create binary image with the detected lines.
Ibw = ~Ihoughlines(:,:,1);
Ibw(Ibw > 0) = true;

% Dilate the image using a disk structuring element.
diskRadius = 10; % Might need tuning depending on the input image.
se = strel('disk', diskRadius);
Idilated = imdilate(Ibw, se);

%------------------------------------------------------------------------
% Localization parameters for the barcode
%------------------------------------------------------------------------

% Compute region properties Orientation and BoundingBox.
regionStatistics = regionprops(Idilated, 'Orientation', 'BoundingBox');

% Padding for the cropped images of barcodes.
padding = 40;

boundingBox = zeros(length(regionStatistics), 4);

for idx = 1:length(regionStatistics)
    
    boundingBox(idx,:) = regionStatistics(idx).BoundingBox;
    
    % Bounding box coordinates with padding.
    boundingBox(idx,1) = boundingBox(idx,1) - padding;
    boundingBox(idx,2) = boundingBox(idx,2) - padding;
    boundingBox(idx,3) = boundingBox(idx,3) + 2*padding;
    boundingBox(idx,4) = boundingBox(idx,4) + 2*padding;
    
end

orientation = [regionStatistics(:).Orientation];

end

Ссылки

[1] Creusot, Clement, et al. «Обнаружение штрих-кода в реальном времени в дикой природе». Зимняя конференция IEEE по применению компьютерного зрения, 2015.